From 347c6b11211127bd9c7db94755d9401cc2f5ba32 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Tue, 30 Jan 2024 16:38:08 +0200 Subject: [PATCH 001/176] QmlDesigner: Add extended 3D project template Extended 3D template use ExtendedSceneEnvironment instead of regular SceneEnvironment to enable various built-in effects. Fixes: QDS-11812 Change-Id: If84807f5a4dd327d6da4f08669b66ed3d7a1ff14 Reviewed-by: Mahmoud Badri Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Thomas Hartmann --- .../Screen01.ui.qml.tpl | 70 ++++ .../contentmodule.main.qml.tpl | 36 ++ .../application-extended-3d/desktop_blank.png | Bin 0 -> 807 bytes .../desktop_blank@2.png | Bin 0 -> 1575 bytes .../application-extended-3d/detailsPage.qml | 8 + .../application-extended-3d/wizard.json | 389 ++++++++++++++++++ 6 files changed, 503 insertions(+) create mode 100644 share/qtcreator/qmldesigner/studio_templates/projects/application-extended-3d/Screen01.ui.qml.tpl create mode 100644 share/qtcreator/qmldesigner/studio_templates/projects/application-extended-3d/contentmodule.main.qml.tpl create mode 100644 share/qtcreator/qmldesigner/studio_templates/projects/application-extended-3d/desktop_blank.png create mode 100644 share/qtcreator/qmldesigner/studio_templates/projects/application-extended-3d/desktop_blank@2.png create mode 100644 share/qtcreator/qmldesigner/studio_templates/projects/application-extended-3d/detailsPage.qml create mode 100644 share/qtcreator/qmldesigner/studio_templates/projects/application-extended-3d/wizard.json diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/application-extended-3d/Screen01.ui.qml.tpl b/share/qtcreator/qmldesigner/studio_templates/projects/application-extended-3d/Screen01.ui.qml.tpl new file mode 100644 index 00000000000..2b26fe3f0ce --- /dev/null +++ b/share/qtcreator/qmldesigner/studio_templates/projects/application-extended-3d/Screen01.ui.qml.tpl @@ -0,0 +1,70 @@ +/* +This is a UI file (.ui.qml) that is intended to be edited in Qt Design Studio only. +It is supposed to be strictly declarative and only uses a subset of QML. If you edit +this file manually, you might introduce QML code that is not supported by Qt Design Studio. +Check out https://doc.qt.io/qtcreator/creator-quick-ui-forms.html for details on .ui.qml files. +*/ + +import QtQuick %{QtQuickVersion} +import QtQuick.Controls %{QtQuickVersion} +import QtQuick3D %{QtQuick3DVersion} +import QtQuick3D.Effects %{QtQuick3DVersion} +import QtQuick3D.Helpers %{QtQuick3DVersion} +import %{ImportModuleName} %{ImportModuleVersion} + +Rectangle { + width: Constants.width + height: Constants.height + + color: Constants.backgroundColor + + View3D { + id: view3D + anchors.fill: parent + + environment: sceneEnvironment + + ExtendedSceneEnvironment { + id: sceneEnvironment + antialiasingMode: SceneEnvironment.MSAA + antialiasingQuality: SceneEnvironment.High + } + + Node { + id: scene + DirectionalLight { + id: directionalLight + } + + PerspectiveCamera { + id: sceneCamera + z: 350 + } + + Model { + id: cubeModel + eulerRotation.y: 45 + eulerRotation.x: 30 + materials: defaultMaterial + source: "#Cube" + } + } + } + + Item { + id: __materialLibrary__ + PrincipledMaterial { + id: defaultMaterial + objectName: "Default Material" + baseColor: "#4aee45" + } + } + + Text { + text: qsTr("Hello %{ProjectName}") + anchors.top: parent.top + anchors.horizontalCenter: parent.horizontalCenter + anchors.topMargin: 100 + font.family: Constants.font.family + } +} diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/application-extended-3d/contentmodule.main.qml.tpl b/share/qtcreator/qmldesigner/studio_templates/projects/application-extended-3d/contentmodule.main.qml.tpl new file mode 100644 index 00000000000..96f5dbaa36c --- /dev/null +++ b/share/qtcreator/qmldesigner/studio_templates/projects/application-extended-3d/contentmodule.main.qml.tpl @@ -0,0 +1,36 @@ +import QtQuick %{QtQuickVersion} +@if !%{IsQt6Project} +import QtQuick.Window %{QtQuickVersion} +@endif +import %{ApplicationImport} +@if %{UseVirtualKeyboard} +import QtQuick.VirtualKeyboard %{QtQuickVersion} +@endif + +Window { + width: Constants.width + height: Constants.height + + visible: true + + Screen01 { + } + +@if %{UseVirtualKeyboard} + InputPanel { + id: inputPanel + property bool showKeyboard : active + y: showKeyboard ? parent.height - height : parent.height + Behavior on y { + NumberAnimation { + duration: 200 + easing.type: Easing.InOutQuad + } + } + anchors.leftMargin: Constants.width/10 + anchors.rightMargin: Constants.width/10 + anchors.left: parent.left + anchors.right: parent.right + } +@endif +} diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/application-extended-3d/desktop_blank.png b/share/qtcreator/qmldesigner/studio_templates/projects/application-extended-3d/desktop_blank.png new file mode 100644 index 0000000000000000000000000000000000000000..7cf79d6d02b277b8ba588d92f8ab3fd2cb54dcd0 GIT binary patch literal 807 zcmeAS@N?(olHy`uVBq!ia0y~yU}$GxU`XI#W?*1osF=vaz`&>(;1lA?z`)SbJ|#Q9 zAv?dJrF{w-f(Sxn;Ut=RxHyCXmxXZQCZLJ8v`;Ck>Vyckv`;Cm?0~38b^t^KVgf`Z z8aq3`At*dMEIKbHp*Sh6A|t0ZGq)}?uP!UEE<3*=r=TIHpfRtgDZjY6u(Y+fysf0X zy{xjMtg5r3x~sCbyQ;RgwxPeNby9O1$m26+FPk-I#j=%~m#^BgdF#F>PhUKH{_6Yp zAHRS9`Sa%wU(JLW3=9koB|(0{42*0XTzmq;l2Xz#a`H+pZUIrTiAl*R=@~`UHT5lB zJ-rhqOkc2g*~ZPAx9{3_?$YHeH*Vg&|K!!Xuiw7^{`2?W9*YHf3=E99o-U3d8TZ~^ zbq_z}Aky}*dyz)R-li2dMBi{}Fy_37NOtT!#`N>Q{UKJ*zT+w_ktvgoe$RVlqPE%>uWzZ>eSe(w=XD3NlW*5w4qwxj1_#x=Smj)q-^%<5fQ$<#Wp#>pgEN7 zjl^8`MY1{j4<9o;dc-{Q@eQNpre9B=zA~#a^YW<|=I!%K&)$Bus&e)DM-ql^^1NqX zOT9m~F|j-On8e?Vd*5?qyd!10e?18lzBsoo_uiw3OX~VgGDb252N>CjA~*_TPAVm> zb$A;Y9m0Nv%R^dFA*6o**x6AD)_nPwj-?NuTS_{7&+iUC}`7VD- z_|IKc@2)&OYy96$)!S2*Lv>|17lZRF#VC#jWnY(vviBu#Cn_zvG4FiO-H6Ycuj9T? z-FPc?&&q`xoASF3WxP0lUv-k+uSFWmlzk8ASL<%BvfQ6H4U_>qUHx3vIVCg!0Bk3E AsQ>@~ literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/application-extended-3d/desktop_blank@2.png b/share/qtcreator/qmldesigner/studio_templates/projects/application-extended-3d/desktop_blank@2.png new file mode 100644 index 0000000000000000000000000000000000000000..3dc3dde8f4d13a4ed2a879122cb3b15ae4a4ef25 GIT binary patch literal 1575 zcmeAS@N?(olHy`uVBq!ia0y~yVB}?BU^u|R%)r2)dG^>71_maL0G|+71_p-g{DzkH zDaa&*o1NbP7lg3kWJ~)LxEfq+xMBzcq8_3cLP89Ou;C;`5JJMWAe(?J1rf(ZLd=BA zLQI2mv-2AoTPH!(G`38H7z-gGf^ZU93xo|Z3Qj^)LZpyQfUw~tCL<_3J2)aIG%`0V zIzKY5Fgm^{I-xi=u_Q6AB00S>EvqIwzoDR{rLeTMsI;}DqNB93qpY%{ysE3by0g5x ztD?HAx~{jjp}(%NzrJZgL(9a*mWhq6lbc#6HMLG|ZUei0+q!-KfkP(`9XWOS%%$@eu04AE?DLneU%q_({{6?# zpTEH1_n$w1tP+=~FfcIGmjw9*GcYnTF)=fCq*RS7y{{GX5 zOWDA{z^w1-;uunK>+Q{-(LR9^2R^R$STbef`kSsDZ>&@Ar=Fg*) zEv{dT7G`-{v2qW+_H6mKuO$>S}o!+LgHW_YlD(u~q@>=39ALmu;8&@|S zOIldhy(a04?b=4SO#g1F)vbJMH?>U+S=Q=SxJb44r&E7g^raJD4I}?AJNmNg${gMG zuWH|Hxz2UWLB3hvez($YfBl-AtEZce@x0&oecg%obH7JT*3HQIFstfy(8B9+)oV8| zD0Gmk`r^ykyZ-x=qu+iU@A$fFQBBFpi|myfC;!v`=K1N&SN*OGi|nUY|Lm9KpX5Go zN65>Kd!p?3OC5ePG5u7A&D3z|H{r=GHlJj8O)fYv4-rBq4=TCGuIi?|@ zx4+S{ETenl-G_e_q>R5$nI@)MZ1t-~;oas)=N{j^|9>RdMQxW)vRwS{+keN9tbJ}` gKUW66z5bssCWxa&=ivJL3=9kmp00i_>zopr02v4EasU7T literal 0 HcmV?d00001 diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/application-extended-3d/detailsPage.qml b/share/qtcreator/qmldesigner/studio_templates/projects/application-extended-3d/detailsPage.qml new file mode 100644 index 00000000000..224b9b1736f --- /dev/null +++ b/share/qtcreator/qmldesigner/studio_templates/projects/application-extended-3d/detailsPage.qml @@ -0,0 +1,8 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import ProjectType + +DefaultProject { + +} diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/application-extended-3d/wizard.json b/share/qtcreator/qmldesigner/studio_templates/projects/application-extended-3d/wizard.json new file mode 100644 index 00000000000..0e3e699a011 --- /dev/null +++ b/share/qtcreator/qmldesigner/studio_templates/projects/application-extended-3d/wizard.json @@ -0,0 +1,389 @@ +{ + "version": 1, + "supportedProjectTypes": [ "QmlProjectManager.QmlProject" ], + "id": "QA.QtStudioUi3DExt", + "category": "B.StudioProject", + "trDescription": "Creates a project that uses default and 3D components, such as camera, light, model, and materials. Extended scene environment will also be included to enable various built-in effects.", + "trDisplayName": "3D Extended", + "trDisplayCategory": "General", + "icon": "desktop_blank.png", + "fontIconName": "wizardsGeneric", + "enabled": "%{JS: [ %{Plugins} ].indexOf('QmlProjectManager') >= 0}", + "platformIndependent": true, + + "options": + [ + { "key": "ProjectExecutableName", "value": "%{ProjectName}App" }, + { "key": "ProjectPluginName", "value": "%{ProjectName}plugin" }, + { "key": "ProjectPluginClassName", "value": "%{ProjectName}Plugin" }, + { "key": "QmlProjectFileName", "value": "%{JS: Util.fileName('%{ProjectName}', 'qmlproject')}" }, + { "key": "IsQt6Project", "value": "%{JS: value('QtQuickVersion') !== '2.15' }" }, + { "key": "ImportModuleName", "value": "%{ProjectName}" }, + { "key": "UIClassName", "value": "Screen01" }, + { "key": "UIClassFileName", "value": "%{JS: Util.fileName('%{UIClassName}', 'ui.qml')}" }, + { "key": "QtQuickVersion", "value": "%{JS: %{TargetQtVersion}.TargetQuickVersion}" }, + { "key": "QtQuickFeature", "value": "QtSupport.Wizards.FeatureQtQuick.%{QtQuickVersion}" }, + { "key": "QtQuickControlsStyle", "value": "Basic" }, + { "key": "QtQuickControlsStyleTheme", "value": "%{JS: %{ControlsStyle}.QtQuickControlsStyleTheme}" }, + { "key": "ApplicationImport", "value": "%{JS: value('IsQt6Project') === 'true' ? '%{ImportModuleName}' : '%{ImportModuleName} 1.0'}" }, + { "key": "UseStandardResolution", "value": "%{JS: value('CustomScreenWidth') === '' || value('CustomScreenHeight') === ''}" }, + { "key": "ScreenWidth", "value": "%{JS: value('UseStandardResolution') === 'true' ? %{ScreenFactor}.ScreenWidth : value('CustomScreenWidth')}" }, + { "key": "ScreenHeight", "value": "%{JS: value('UseStandardResolution') === 'true' ? %{ScreenFactor}.ScreenHeight : value('CustomScreenHeight')}" }, + { "key": "UseVirtualKeyboardDefault", "value": "%{JS: false}" }, + { "key": "QtQuick3DVersion", "value": "%{JS: %{TargetQtVersion}.TargetQuick3DVersion}" }, + { "key": "ImportModuleVersion", "value": "%{JS: value('IsQt6Project') === 'true' ? '' : '1.0'}" } + ], + + "pages": + [ + { + "defaultValues": "qmlapplication-project-page" + }, + { + "trDisplayName": "Define Project Details", + "trShortTitle": "Details", + "typeId": "Fields", + "data": + [ + { + "name": "ScreenFactor", + "trDisplayName": "Screen Resolution:", + "type": "ComboBox", + "enabled": "%{JS: value('UseStandardResolution')}", + "data": + { + "index": 2, + "items": + [ + { + "trKey": "2960 x 1440", + "value": + "({ + 'ScreenWidth': '2960', + 'ScreenHeight': '1440' + })" + }, + { + "trKey": "2560 x 1440", + "value": + "({ + 'ScreenWidth': '2560', + 'ScreenHeight': '1440' + })" + }, + { + "trKey": "1920 x 1080", + "value": + "({ + 'ScreenWidth': '1920', + 'ScreenHeight': '1080' + })" + }, + { + "trKey": "1334 x 750", + "value": + "({ + 'ScreenWidth': '1334', + 'ScreenHeight': '750' + })" + }, + { + "trKey": "1280 x 960", + "value": + "({ + 'ScreenWidth': '1280', + 'ScreenHeight': '960' + })" + }, + { + "trKey": "1280 x 720", + "value": + "({ + 'ScreenWidth': '1280', + 'ScreenHeight': '720' + })" + }, + { + "trKey": "1024 x 768", + "value": + "({ + 'ScreenWidth': '1024', + 'ScreenHeight': '768' + })" + }, + { + "trKey": "640 x 480", + "value": + "({ + 'ScreenWidth': '640', + 'ScreenHeight': '480' + })" + } + ] + } + }, + { + "name": "ControlsStyle", + "trDisplayName": "Qt Quick Controls Style:", + "type": "ComboBox", + "data": + { + "index": 0, + "items": + [ + { + "trKey": "Basic", + "value": + "({ + 'QtQuickControlsStyle': 'Basic', + 'QtQuickControlsStyleTheme': '' + })" + }, + { + "trKey": "Material Light", + "value": + "({ + 'QtQuickControlsStyle': 'Material', + 'QtQuickControlsStyleTheme': 'Light' + })" + }, + { + "trKey": "Material Dark", + "value": + "({ + 'QtQuickControlsStyle': 'Material', + 'QtQuickControlsStyleTheme': 'Dark' + })" + }, + { + "trKey": "Universal Light", + "value": + "({ + 'QtQuickControlsStyle': 'Universal', + 'QtQuickControlsStyleTheme': 'Light' + })" + }, + { + "trKey": "Universal Dark", + "value": + "({ + 'QtQuickControlsStyle': 'Universal', + 'QtQuickControlsStyleTheme': 'Dark' + })" + }, + { + "trKey": "Universal System", + "value": + "({ + 'QtQuickControlsStyle': 'Universal', + 'QtQuickControlsStyleTheme': 'System' + })" + }, + { + "trKey": "Fusion", + "value": + "({ + 'QtQuickControlsStyle': 'Fusion', + 'QtQuickControlsStyleTheme': '' + })" + }, + { + "trKey": "Imagine", + "value": + "({ + 'QtQuickControlsStyle': 'Imagine', + 'QtQuickControlsStyleTheme': '' + })" + } + ] + } + }, + { + "name": "UseVirtualKeyboard", + "trDisplayName": "Use Qt Virtual Keyboard", + "type": "CheckBox", + "data": + { + "checked": "%{UseVirtualKeyboardDefault}" + } + }, + { + "name": "CustomScreenWidth", + "trDisplayName": "Custom screen width:", + "mandatory": false, + "type": "LineEdit", + "data": + { + "validator": "^[0-9]*$", + "trText": "" + } + }, + { + "name": "CustomScreenHeight", + "trDisplayName": "Custom screen height:", + "mandatory": false, + "type": "LineEdit", + "data": + { + "validator": "^[0-9]*$", + "trText": "" + } + }, + { + "name": "TargetQtVersion", + "trDisplayName": "Target Qt Version:", + "mandatory": true, + "type": "ComboBox", + "data": + { + "index": 3, + "items": + [ + { + "trKey": "Qt 6.2", + "value": + "({ + 'TargetQuickVersion': '6.2', + 'TargetQuick3DVersion': '6.2' + })" + }, + { + "trKey": "Qt 6.3", + "value": + "({ + 'TargetQuickVersion': '6.3', + 'TargetQuick3DVersion': '6.3' + })" + }, + { + "trKey": "Qt 6.4", + "value": + "({ + 'TargetQuickVersion': '6.4', + 'TargetQuick3DVersion': '6.4' + })" + }, + { + "trKey": "Qt 6.5", + "value": + "({ + 'TargetQuickVersion': '6.5', + 'TargetQuick3DVersion': '6.5' + })" + } + ] + } + } + ] + } + + ], + "generators": + [ + { + "typeId": "File", + "data": + [ + { + "source": "../common/app.qmlproject.tpl", + "target": "%{ProjectDirectory}/%{QmlProjectFileName}", + "openAsProject": true + }, + { + "source": "../common/CMakeLists.main.txt.tpl", + "target": "%{ProjectDirectory}/CMakeLists.txt" + }, + { + "source": "../common/qmlmodules.tpl", + "target": "%{ProjectDirectory}/qmlmodules" + }, + { + "source": "../common/qmlcomponents.tpl", + "target": "%{ProjectDirectory}/qmlcomponents" + }, + { + "source": "../common/insight.tpl", + "target": "%{ProjectDirectory}/insight" + }, + { + "source": "../common/main.qml", + "target": "%{ProjectDirectory}/main.qml" + }, + { + "source": "../common/qtquickcontrols2.conf.tpl", + "target": "%{ProjectDirectory}/qtquickcontrols2.conf" + }, + { + "source": "../common/main.cpp.tpl", + "target": "%{ProjectDirectory}/src/main.cpp" + }, + { + "source": "../common/app_environment.h.tpl", + "target": "%{ProjectDirectory}/src/app_environment.h" + }, + { + "source": "../common/import_qml_plugins.h.tpl", + "target": "%{ProjectDirectory}/src/import_qml_plugins.h" + }, + { + "source": "../common/import_qml_components_plugins.h.tpl", + "target": "%{ProjectDirectory}/src/import_qml_components_plugins.h" + }, + + { + "source": "../common/CMakeLists.content.txt.tpl", + "target": "%{ProjectDirectory}/content/CMakeLists.txt" + }, + { + "source": "../common/App.qml.tpl", + "target": "%{ProjectDirectory}/content/App.qml" + }, + { + "source": "Screen01.ui.qml.tpl", + "target": "%{ProjectDirectory}/content/Screen01.ui.qml", + "openInEditor": true + }, + { + "source": "../common/fonts.txt", + "target": "%{ProjectDirectory}/content/fonts/fonts.txt" + }, + { + "source": "../common/asset_imports.txt", + "target": "%{ProjectDirectory}/asset_imports/asset_imports.txt" + }, + { + "source": "../common/CMakeLists.imports.txt.tpl", + "target": "%{ProjectDirectory}/imports/CMakeLists.txt" + }, + { + "source": "../shared-plugin/name/CMakeLists.importmodule.txt.tpl", + "target": "%{ProjectDirectory}/imports/%{ImportModuleName}/CMakeLists.txt" + }, + { + "source": "../shared-plugin/name/importmodule.qmldir.tpl", + "target": "%{ProjectDirectory}/imports/%{ImportModuleName}/qmldir" + }, + { + "source": "../shared-plugin/name/Constants.qml.tpl", + "target": "%{ProjectDirectory}/imports/%{ImportModuleName}/Constants.qml" + }, + { + "source": "../shared-plugin/name/DirectoryFontLoader.qml.tpl", + "target": "%{ProjectDirectory}/imports/%{ImportModuleName}/DirectoryFontLoader.qml" + }, + { + "source": "../shared-plugin/name/EventListModel.qml.tpl", + "target": "%{ProjectDirectory}/imports/%{ImportModuleName}/EventListModel.qml" + }, + { + "source": "../shared-plugin/name/EventListSimulator.qml.tpl", + "target": "%{ProjectDirectory}/imports/%{ImportModuleName}/EventListSimulator.qml" + }, + { + "source": "../shared-plugin/name/designer/plugin.metainfo", + "target": "%{ProjectDirectory}/imports/%{ImportModuleName}/designer/plugin.metainfo" + } + ] + } + ] +} From 6a4df2b6e698b386a2650a10be9ce8c229d5ae34 Mon Sep 17 00:00:00 2001 From: Shrief Gabr Date: Wed, 31 Jan 2024 16:47:17 +0200 Subject: [PATCH 002/176] QmlDesigner: Fix unresponsive RealSpinBox buttons in Number cells Task-number: QDS-11755 Change-Id: I53a7422642202acbea043800bd91e913b7bd0779 Reviewed-by: Miikka Heikkinen --- .../collectionEditorQmlSource/CollectionDetailsEditDelegate.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsEditDelegate.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsEditDelegate.qml index d3285462609..6a3146a8188 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsEditDelegate.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsEditDelegate.qml @@ -149,7 +149,7 @@ Item { target: editorPopup.editor function onActiveFocusChanged() { - if (!editorPopup.editor.activeFocus) + if (!editorPopup.activeFocus) editorPopup.close() else if (edit) editorPopup.editor.editValue = edit From bcb231b37c9584f64885569d7bcdcd88afba745d Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Thu, 1 Feb 2024 13:33:04 +0200 Subject: [PATCH 003/176] EffectComposer: Don't reset view when switching files Fixes: QDS-11713 Change-Id: Ic38d2fdf5105a5ecd623b8624fa028f6a45fe207 Reviewed-by: Miikka Heikkinen --- src/plugins/effectcomposer/effectcomposerview.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/effectcomposer/effectcomposerview.cpp b/src/plugins/effectcomposer/effectcomposerview.cpp index 3c1901a3d4c..ab7da90e1d7 100644 --- a/src/plugins/effectcomposer/effectcomposerview.cpp +++ b/src/plugins/effectcomposer/effectcomposerview.cpp @@ -79,19 +79,19 @@ void EffectComposerView::modelAttached(QmlDesigner::Model *model) { AbstractView::modelAttached(model); - m_widget->effectComposerNodesModel()->loadModel(); QString currProjectPath = QmlDesigner::DocumentManager::currentProjectDirPath().toString(); if (m_currProjectPath != currProjectPath) { // starting a new project + m_widget->effectComposerNodesModel()->loadModel(); m_widget->effectComposerModel()->clear(true); m_widget->effectComposerModel()->setIsEnabled( !QmlDesigner::DesignerMcuManager::instance().isMCUProject()); + m_widget->initView(); } m_currProjectPath = currProjectPath; - m_widget->initView(); } void EffectComposerView::modelAboutToBeDetached(QmlDesigner::Model *model) From 2a7c82c7ea89e8a01704d3a9f6e21160fbfd6076 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Thu, 1 Feb 2024 14:27:45 +0200 Subject: [PATCH 004/176] EffectComposer: Make spinboxes HelperWidgets.DoubleSpinBoxes This way we get proper press-and-drag behavior for them. Fixes: QDS-11750 Change-Id: I039d72933b533e83ebebf0da36e5c0db81908689 Reviewed-by: Mahmoud Badri Reviewed-by: Qt CI Patch Build Bot --- .../effectComposerQmlSources/ValueFloat.qml | 16 +++--- .../effectComposerQmlSources/ValueInt.qml | 12 +++-- .../effectComposerQmlSources/ValueVec2.qml | 27 +++++----- .../effectComposerQmlSources/ValueVec3.qml | 40 +++++++------- .../effectComposerQmlSources/ValueVec4.qml | 53 +++++++++---------- .../imports/HelperWidgets/DoubleSpinBox.qml | 2 + 6 files changed, 74 insertions(+), 76 deletions(-) diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueFloat.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueFloat.qml index 61c5dca825f..f33d33d9c9d 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueFloat.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueFloat.qml @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 import QtQuick +import HelperWidgets as HelperWidgets import StudioControls as StudioControls import StudioTheme as StudioTheme import EffectComposerBackend @@ -10,19 +11,18 @@ Row { width: parent.width spacing: 5 - StudioControls.RealSpinBox { + HelperWidgets.DoubleSpinBox { id: spinBox width: 60 - actionIndicatorVisible: false spinBoxIndicatorVisible: false inputHAlignment: Qt.AlignHCenter - realFrom: uniformMinValue - realTo: uniformMaxValue - realValue: uniformValue - realStepSize: .01 + minimumValue: uniformMinValue + maximumValue: uniformMaxValue + value: uniformValue + stepSize: .01 decimals: 2 - onRealValueModified: uniformValue = realValue + onValueModified: uniformValue = value } StudioControls.Slider { @@ -39,7 +39,7 @@ Row { value: uniformValue onMoved: { uniformValue = value - spinBox.realValue = value // binding isn't working for this property so update it + spinBox.value = value // binding isn't working for this property so update it } } } diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueInt.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueInt.qml index 86ba9ba78d4..5fec6b1de18 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueInt.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueInt.qml @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 import QtQuick +import HelperWidgets as HelperWidgets import StudioControls as StudioControls import StudioTheme as StudioTheme import EffectComposerBackend @@ -10,17 +11,18 @@ Row { width: parent.width spacing: 5 - StudioControls.SpinBox { + HelperWidgets.DoubleSpinBox { id: spinBox width: 60 - actionIndicatorVisible: false spinBoxIndicatorVisible: false inputHAlignment: Qt.AlignHCenter - from: uniformMinValue - to: uniformMaxValue + minimumValue: uniformMinValue + maximumValue: uniformMaxValue value: uniformValue - onValueModified: uniformValue = value + stepSize: 1 + decimals: 0 + onValueModified: uniformValue = Math.round(value) } StudioControls.Slider { diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueVec2.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueVec2.qml index 0685b5f41a2..cd6a4330247 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueVec2.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueVec2.qml @@ -3,6 +3,7 @@ import QtQuick import QtQuick.Layouts +import HelperWidgets as HelperWidgets import StudioControls as StudioControls import StudioTheme as StudioTheme import EffectComposerBackend @@ -11,22 +12,21 @@ RowLayout { width: parent.width spacing: 0 - StudioControls.RealSpinBox { + HelperWidgets.DoubleSpinBox { id: vX Layout.fillWidth: true Layout.minimumWidth: 30 Layout.maximumWidth: 60 - actionIndicatorVisible: false spinBoxIndicatorVisible: false inputHAlignment: Qt.AlignHCenter - realFrom: uniformMinValue.x - realTo: uniformMaxValue.x - realValue: uniformValue.x - realStepSize: .01 + minimumValue: uniformMinValue.x + maximumValue: uniformMaxValue.x + value: uniformValue.x + stepSize: .01 decimals: 2 - onRealValueModified: uniformValue.x = realValue + onValueModified: uniformValue.x = value } Item { // spacer @@ -48,22 +48,21 @@ RowLayout { Layout.maximumWidth: 20 } - StudioControls.RealSpinBox { + HelperWidgets.DoubleSpinBox { id: vY Layout.fillWidth: true Layout.minimumWidth: 30 Layout.maximumWidth: 60 - actionIndicatorVisible: false spinBoxIndicatorVisible: false inputHAlignment: Qt.AlignHCenter - realFrom: uniformMinValue.y - realTo: uniformMaxValue.y - realValue: uniformValue.y - realStepSize: .01 + minimumValue: uniformMinValue.y + maximumValue: uniformMaxValue.y + value: uniformValue.y + stepSize: .01 decimals: 2 - onRealValueModified: uniformValue.y = realValue + onValueModified: uniformValue.y = value } Item { // spacer diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueVec3.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueVec3.qml index bb33cb68e4f..e7ab2ed965e 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueVec3.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueVec3.qml @@ -3,6 +3,7 @@ import QtQuick import QtQuick.Layouts +import HelperWidgets as HelperWidgets import StudioControls as StudioControls import StudioTheme as StudioTheme import EffectComposerBackend @@ -11,22 +12,21 @@ RowLayout { width: parent.width spacing: 0 - StudioControls.RealSpinBox { + HelperWidgets.DoubleSpinBox { id: vX Layout.fillWidth: true Layout.minimumWidth: 30 Layout.maximumWidth: 60 - actionIndicatorVisible: false spinBoxIndicatorVisible: false inputHAlignment: Qt.AlignHCenter - realFrom: uniformMinValue.x - realTo: uniformMaxValue.x - realValue: uniformValue.x - realStepSize: .01 + minimumValue: uniformMinValue.x + maximumValue: uniformMaxValue.x + value: uniformValue.x + stepSize: .01 decimals: 2 - onRealValueModified: uniformValue.x = realValue + onValueModified: uniformValue.x = value } Item { // spacer @@ -48,22 +48,21 @@ RowLayout { Layout.maximumWidth: 20 } - StudioControls.RealSpinBox { + HelperWidgets.DoubleSpinBox { id: vY Layout.fillWidth: true Layout.minimumWidth: 30 Layout.maximumWidth: 60 - actionIndicatorVisible: false spinBoxIndicatorVisible: false inputHAlignment: Qt.AlignHCenter - realFrom: uniformMinValue.y - realTo: uniformMaxValue.y - realValue: uniformValue.y - realStepSize: .01 + minimumValue: uniformMinValue.y + maximumValue: uniformMaxValue.y + value: uniformValue.y + stepSize: .01 decimals: 2 - onRealValueModified: uniformValue.y = realValue + onValueModified: uniformValue.y = value } Item { // spacer @@ -85,22 +84,21 @@ RowLayout { Layout.maximumWidth: 20 } - StudioControls.RealSpinBox { + HelperWidgets.DoubleSpinBox { id: vZ Layout.fillWidth: true Layout.minimumWidth: 30 Layout.maximumWidth: 60 - actionIndicatorVisible: false spinBoxIndicatorVisible: false inputHAlignment: Qt.AlignHCenter - realFrom: uniformMinValue.z - realTo: uniformMaxValue.z - realValue: uniformValue.z - realStepSize: .01 + minimumValue: uniformMinValue.z + maximumValue: uniformMaxValue.z + value: uniformValue.z + stepSize: .01 decimals: 2 - onRealValueModified: uniformValue.z = realValue + onValueModified: uniformValue.z = value } Item { // spacer diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueVec4.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueVec4.qml index bcb35161466..404ccad5936 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueVec4.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueVec4.qml @@ -3,6 +3,7 @@ import QtQuick import QtQuick.Layouts +import HelperWidgets as HelperWidgets import StudioControls as StudioControls import StudioTheme as StudioTheme import EffectComposerBackend @@ -11,22 +12,21 @@ RowLayout { width: parent.width spacing: 0 - StudioControls.RealSpinBox { + HelperWidgets.DoubleSpinBox { id: vX Layout.fillWidth: true Layout.minimumWidth: 30 Layout.maximumWidth: 60 - actionIndicatorVisible: false spinBoxIndicatorVisible: false inputHAlignment: Qt.AlignHCenter - realFrom: uniformMinValue.x - realTo: uniformMaxValue.x - realValue: uniformValue.x - realStepSize: .01 + minimumValue: uniformMinValue.x + maximumValue: uniformMaxValue.x + value: uniformValue.x + stepSize: .01 decimals: 2 - onRealValueModified: uniformValue.x = realValue + onValueModified: uniformValue.x = value } Item { // spacer @@ -48,22 +48,21 @@ RowLayout { Layout.maximumWidth: 20 } - StudioControls.RealSpinBox { + HelperWidgets.DoubleSpinBox { id: vY Layout.fillWidth: true Layout.minimumWidth: 30 Layout.maximumWidth: 60 - actionIndicatorVisible: false spinBoxIndicatorVisible: false inputHAlignment: Qt.AlignHCenter - realFrom: uniformMinValue.y - realTo: uniformMaxValue.y - realValue: uniformValue.y - realStepSize: .01 + minimumValue: uniformMinValue.y + maximumValue: uniformMaxValue.y + value: uniformValue.y + stepSize: .01 decimals: 2 - onRealValueModified: uniformValue.y = realValue + onValueModified: uniformValue.y = value } Item { // spacer @@ -85,22 +84,21 @@ RowLayout { Layout.maximumWidth: 20 } - StudioControls.RealSpinBox { + HelperWidgets.DoubleSpinBox { id: vZ Layout.fillWidth: true Layout.minimumWidth: 30 Layout.maximumWidth: 60 - actionIndicatorVisible: false spinBoxIndicatorVisible: false inputHAlignment: Qt.AlignHCenter - realFrom: uniformMinValue.z - realTo: uniformMaxValue.z - realValue: uniformValue.z - realStepSize: .01 + minimumValue: uniformMinValue.z + maximumValue: uniformMaxValue.z + value: uniformValue.z + stepSize: .01 decimals: 2 - onRealValueModified: uniformValue.z = realValue + onValueModified: uniformValue.z = value } Item { // spacer @@ -122,22 +120,21 @@ RowLayout { Layout.maximumWidth: 20 } - StudioControls.RealSpinBox { + HelperWidgets.DoubleSpinBox { id: vW Layout.fillWidth: true Layout.minimumWidth: 30 Layout.maximumWidth: 60 - actionIndicatorVisible: false spinBoxIndicatorVisible: false inputHAlignment: Qt.AlignHCenter - realFrom: uniformMinValue.w - realTo: uniformMaxValue.w - realValue: uniformValue.w - realStepSize: .01 + minimumValue: uniformMinValue.w + maximumValue: uniformMaxValue.w + value: uniformValue.w + stepSize: .01 decimals: 2 - onRealValueModified: uniformValue.w = realValue + onValueModified: uniformValue.w = value } Item { // spacer diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/DoubleSpinBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/DoubleSpinBox.qml index 115c41e103d..a44e8c690be 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/DoubleSpinBox.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/DoubleSpinBox.qml @@ -13,8 +13,10 @@ Item { property alias minimumValue: spinBox.realFrom property alias maximumValue: spinBox.realTo property alias stepSize: spinBox.realStepSize + property alias spinBoxIndicatorVisible: spinBox.spinBoxIndicatorVisible property alias sliderIndicatorVisible: spinBox.sliderIndicatorVisible property alias hover: spinBox.hover + property alias inputHAlignment: spinBox.inputHAlignment property alias pixelsPerUnit: spinBox.pixelsPerUnit From 6a910131c6f1de2c61c4ebe1919ced86659a0a0a Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Wed, 15 Nov 2023 17:31:43 +0100 Subject: [PATCH 005/176] QmlDesigner: Support Qt 6.6 in wizards Change-Id: Ie7baf06b9a8e7962bcd805df2ed9bf5c1066bdee Reviewed-by: Mahmoud Badri Reviewed-by: Miikka Heikkinen --- .../studio_templates/projects/application-3d/wizard.json | 8 ++++++++ .../projects/application-extended-3d/wizard.json | 8 ++++++++ .../studio_templates/projects/application/wizard.json | 7 +++++++ .../projects/desktop-launcher/wizard.json | 7 +++++++ .../studio_templates/projects/mobile-scroll/wizard.json | 7 +++++++ .../studio_templates/projects/mobile-stack/wizard.json | 7 +++++++ .../studio_templates/projects/mobile-swipe/wizard.json | 7 +++++++ 7 files changed, 51 insertions(+) diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/application-3d/wizard.json b/share/qtcreator/qmldesigner/studio_templates/projects/application-3d/wizard.json index d5b1fef1e45..42d0bf748c8 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/application-3d/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/application-3d/wizard.json @@ -270,6 +270,14 @@ 'TargetQuickVersion': '6.5', 'TargetQuick3DVersion': '6.5' })" + }, + { + "trKey": "Qt 6.6", + "value": + "({ + 'TargetQuickVersion': '6.6', + 'TargetQuick3DVersion': '6.6' + })" } ] } diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/application-extended-3d/wizard.json b/share/qtcreator/qmldesigner/studio_templates/projects/application-extended-3d/wizard.json index 0e3e699a011..101d5c69032 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/application-extended-3d/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/application-extended-3d/wizard.json @@ -270,6 +270,14 @@ 'TargetQuickVersion': '6.5', 'TargetQuick3DVersion': '6.5' })" + }, + { + "trKey": "Qt 6.6", + "value": + "({ + 'TargetQuickVersion': '6.6', + 'TargetQuick3DVersion': '6.6' + })" } ] } diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/application/wizard.json b/share/qtcreator/qmldesigner/studio_templates/projects/application/wizard.json index 0708838a5ae..c8b74dec498 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/application/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/application/wizard.json @@ -273,6 +273,13 @@ "({ 'TargetQuickVersion': '6.5' })" + }, + { + "trKey": "Qt 6.6", + "value": + "({ + 'TargetQuickVersion': '6.6' + })" } ] } diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/desktop-launcher/wizard.json b/share/qtcreator/qmldesigner/studio_templates/projects/desktop-launcher/wizard.json index 913eb0bf28b..01a603eb9d8 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/desktop-launcher/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/desktop-launcher/wizard.json @@ -271,6 +271,13 @@ "({ 'TargetQuickVersion': '6.5' })" + }, + { + "trKey": "Qt 6.6", + "value": + "({ + 'TargetQuickVersion': '6.6' + })" } ] } diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/mobile-scroll/wizard.json b/share/qtcreator/qmldesigner/studio_templates/projects/mobile-scroll/wizard.json index 944b6b6289c..d521cc201e8 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/mobile-scroll/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/mobile-scroll/wizard.json @@ -230,6 +230,13 @@ "({ 'TargetQuickVersion': '6.5' })" + }, + { + "trKey": "Qt 6.6", + "value": + "({ + 'TargetQuickVersion': '6.6' + })" } ] } diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/mobile-stack/wizard.json b/share/qtcreator/qmldesigner/studio_templates/projects/mobile-stack/wizard.json index c8733770e07..17cd30d507f 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/mobile-stack/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/mobile-stack/wizard.json @@ -228,6 +228,13 @@ "({ 'TargetQuickVersion': '6.5' })" + }, + { + "trKey": "Qt 6.6", + "value": + "({ + 'TargetQuickVersion': '6.6' + })" } ] } diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/mobile-swipe/wizard.json b/share/qtcreator/qmldesigner/studio_templates/projects/mobile-swipe/wizard.json index 295c85aa63a..37710ae91dd 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/mobile-swipe/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/mobile-swipe/wizard.json @@ -228,6 +228,13 @@ "({ 'TargetQuickVersion': '6.5' })" + }, + { + "trKey": "Qt 6.6", + "value": + "({ + 'TargetQuickVersion': '6.6' + })" } ] } From df0125547db2b8aaa8b839fa7b47b2c21a1a7280 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 1 Feb 2024 15:30:50 +0100 Subject: [PATCH 006/176] Fix typo in template Change-Id: Ie3e82fd1f11e3e8349ccf2df2df256aacdc8dd0d Reviewed-by: Knud Dollereder --- .../projects/common/import_qml_components_plugins.h.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/common/import_qml_components_plugins.h.tpl b/share/qtcreator/qmldesigner/studio_templates/projects/common/import_qml_components_plugins.h.tpl index 25d0a98384e..167481d7c74 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/common/import_qml_components_plugins.h.tpl +++ b/share/qtcreator/qmldesigner/studio_templates/projects/common/import_qml_components_plugins.h.tpl @@ -5,7 +5,7 @@ #include "qqmlextensionplugin.h" -#ifdef BULD_QDS_COMPONENTS +#ifdef BUILD_QDS_COMPONENTS Q_IMPORT_QML_PLUGIN(QtQuick_Studio_ComponentsPlugin) Q_IMPORT_QML_PLUGIN(QtQuick_Studio_EffectsPlugin) From b894ae3d16dee1cb2d53d26c8e60cb3f6cc5b283 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 1 Feb 2024 15:52:48 +0100 Subject: [PATCH 007/176] QmlDesigner: Disable exports not supported for MCU Add helper function getStartupBuildSystem() to avoid code duplication. Task-number: QDS-11544 Change-Id: Id8d752d63171838ac9ea6402ff9f2b179d0d8f53 Reviewed-by: Aleksei German --- src/plugins/qmldesigner/generateresource.cpp | 10 +++++++--- .../buildsystem/qmlbuildsystem.cpp | 11 +++++++++++ .../buildsystem/qmlbuildsystem.h | 2 ++ .../cmakegen/cmakeprojectconverter.cpp | 13 ++++++++++--- .../cmakegen/generatecmakelists.cpp | 17 ++++------------- 5 files changed, 34 insertions(+), 19 deletions(-) diff --git a/src/plugins/qmldesigner/generateresource.cpp b/src/plugins/qmldesigner/generateresource.cpp index de8cd5a80b4..63370a82f18 100644 --- a/src/plugins/qmldesigner/generateresource.cpp +++ b/src/plugins/qmldesigner/generateresource.cpp @@ -16,6 +16,7 @@ #include #include +#include #include #include @@ -227,9 +228,12 @@ void GenerateResource::generateMenuEntry(QObject *parent) action->setEnabled(ProjectExplorer::ProjectManager::startupProject() != nullptr); // todo make it more intelligent when it gets enabled QObject::connect(ProjectExplorer::ProjectManager::instance(), - &ProjectExplorer::ProjectManager::startupProjectChanged, [action]() { - action->setEnabled(ProjectExplorer::ProjectManager::startupProject()); - }); + &ProjectExplorer::ProjectManager::startupProjectChanged, + [action]() { + if (auto buildSystem + = QmlProjectManager::QmlBuildSystem::getStartupBuildSystem()) + action->setEnabled(!buildSystem->qtForMCUs()); + }); Core::Command *cmd = Core::ActionManager::registerAction(action, "QmlProject.CreateResource"); QObject::connect(action, &QAction::triggered, [] () { diff --git a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp index 4016cc6ac6f..5131d9c57cc 100644 --- a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp +++ b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp @@ -32,6 +32,7 @@ #include #include +#include "projectexplorer/projectmanager.h" #include "projectitem/qmlprojectitem.h" #include "projectnode/qmlprojectnodes.h" @@ -389,6 +390,16 @@ Utils::FilePath QmlBuildSystem::getStartupQmlFileWithFallback() const return {}; } +QmlBuildSystem *QmlBuildSystem::getStartupBuildSystem() +{ + auto project = ProjectExplorer::ProjectManager::startupProject(); + if (project && project->activeTarget() && project->activeTarget()->buildSystem()) { + return qobject_cast( + project->activeTarget()->buildSystem()); + } + return nullptr; +} + Utils::FilePath QmlBuildSystem::mainFilePath() const { const QString fileName = mainFile(); diff --git a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.h b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.h index 95b66871eb7..3615979e2df 100644 --- a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.h +++ b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.h @@ -102,6 +102,8 @@ public: Utils::FilePath getStartupQmlFileWithFallback() const; + static QmlBuildSystem *getStartupBuildSystem(); + signals: void projectChanged(); diff --git a/src/plugins/qmlprojectmanager/cmakegen/cmakeprojectconverter.cpp b/src/plugins/qmlprojectmanager/cmakegen/cmakeprojectconverter.cpp index 21a538c7c51..232d822cd5d 100644 --- a/src/plugins/qmlprojectmanager/cmakegen/cmakeprojectconverter.cpp +++ b/src/plugins/qmlprojectmanager/cmakegen/cmakeprojectconverter.cpp @@ -15,6 +15,7 @@ #include #include +#include #include #include @@ -44,9 +45,15 @@ void CmakeProjectConverter::generateMenuEntry(QObject *parent) action->setEnabled(isProjectConvertable(ProjectExplorer::ProjectManager::startupProject())); QObject::connect(ProjectExplorer::ProjectManager::instance(), - &ProjectExplorer::ProjectManager::startupProjectChanged, [action]() { - action->setEnabled(isProjectConvertable(ProjectExplorer::ProjectManager::startupProject())); - }); + &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) diff --git a/src/plugins/qmlprojectmanager/cmakegen/generatecmakelists.cpp b/src/plugins/qmlprojectmanager/cmakegen/generatecmakelists.cpp index cee9eb247e6..76452c3e782 100644 --- a/src/plugins/qmlprojectmanager/cmakegen/generatecmakelists.cpp +++ b/src/plugins/qmlprojectmanager/cmakegen/generatecmakelists.cpp @@ -16,6 +16,7 @@ #include #include +#include #include #include #include @@ -85,16 +86,6 @@ enum ProjectDirectoryError { const QString MENU_ITEM_GENERATE = Tr::tr("Generate CMake Build Files..."); -const QmlBuildSystem *getBuildSystem() -{ - auto project = ProjectExplorer::ProjectManager::startupProject(); - if (project && project->activeTarget() && project->activeTarget()->buildSystem()) { - return qobject_cast( - project->activeTarget()->buildSystem()); - } - return nullptr; -} - void generateMenuEntry(QObject *parent) { Core::ActionContainer *menu = Core::ActionManager::actionContainer(Core::Constants::M_FILE); @@ -118,7 +109,7 @@ void generateMenuEntry(QObject *parent) QObject::connect(ProjectExplorer::ProjectManager::instance(), &ProjectExplorer::ProjectManager::startupProjectChanged, [action]() { - if (auto buildSystem = getBuildSystem()) + if (auto buildSystem = QmlBuildSystem::getStartupBuildSystem()) action->setEnabled(!buildSystem->qtForMCUs()); }); } @@ -284,7 +275,7 @@ const QString projectEnvironmentVariable(const QString &key) { QString value = {}; - if (auto buildSystem = getBuildSystem()) { + if (auto buildSystem = QmlBuildSystem::getStartupBuildSystem()) { auto envItems = buildSystem->environment(); auto confEnv = std::find_if(envItems.begin(), envItems.end(), [key](NameValueItem &item) { return item.name == key; @@ -636,7 +627,7 @@ bool CmakeFileGenerator::generateMainCpp(const FilePath &dir) bool envHeaderOk = true; QString environment; - if (auto buildSystem = getBuildSystem()) { + if (auto buildSystem = QmlBuildSystem::getStartupBuildSystem()) { for (EnvironmentItem &envItem : buildSystem->environment()) { QString key = envItem.name; QString value = envItem.value; From 6c7af1b41b3e462621caebcf04addc4b1bf546af Mon Sep 17 00:00:00 2001 From: Brook Cronin Date: Wed, 8 Nov 2023 13:48:08 +0100 Subject: [PATCH 008/176] Qml Designer: Add start and end angle properties to dial Change-Id: I15621fef552816a018aa5adf285ad9a1261a9294 Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Patch Build Bot --- .../QtQuick/Controls/DialSpecifics.qml | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/DialSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/DialSpecifics.qml index a8536e8ca31..92ab3a42ff6 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/DialSpecifics.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/DialSpecifics.qml @@ -104,6 +104,50 @@ Column { ExpandingSpacer {} } + PropertyLabel { + text: qsTr("Start angle") + tooltip: qsTr("This property holds the starting angle of the dial in degrees.") + visible: majorQtQuickVersion === 6 && minorQtQuickVersion >= 6 + } + + SecondColumnLayout { + SpinBox { + implicitWidth: StudioTheme.Values.twoControlColumnWidth + + StudioTheme.Values.actionIndicatorWidth + width: implicitWidth + maximumValue: backendValues.endAngle.value - 1 + minimumValue: Math.min (-360, (backendValues.endAngle.value - 360)) + decimals: 2 + stepSize: 0.1 + backendValue: backendValues.startAngle + visible: majorQtQuickVersion === 6 && minorQtQuickVersion >= 6 + } + + ExpandingSpacer {} + } + + PropertyLabel { + text: qsTr("End angle") + tooltip: qsTr("This property holds the ending angle of the dial in degrees.") + visible: majorQtQuickVersion === 6 && minorQtQuickVersion >= 6 + } + + SecondColumnLayout { + SpinBox { + implicitWidth: StudioTheme.Values.twoControlColumnWidth + + StudioTheme.Values.actionIndicatorWidth + width: implicitWidth + maximumValue: Math.min (720, (backendValues.startAngle.value + 360)) + minimumValue: backendValues.startAngle.value + 1 + decimals: 2 + stepSize: 0.1 + backendValue: backendValues.endAngle + visible: majorQtQuickVersion === 6 && minorQtQuickVersion >= 6 + } + + ExpandingSpacer {} + } + PropertyLabel { text: qsTr("Snap mode") tooltip: qsTr("Sets how the dial's handle snaps to the steps\n" From aaf1e193a766c80f7e1cc61c033a0bc1e96009ae Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Fri, 2 Feb 2024 12:27:51 +0200 Subject: [PATCH 009/176] EffectComposer: implement interactive preview Add panning and pivotted zoom Fixes: QDS-11718 Change-Id: I516474a2ae700b8d1d486e0be96df27b3308e79d Reviewed-by: Miikka Heikkinen --- .../EffectComposerPreview.qml | 133 +++++++++++++++++- 1 file changed, 128 insertions(+), 5 deletions(-) diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerPreview.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerPreview.qml index 84c521aac97..d3170c9d275 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerPreview.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerPreview.qml @@ -20,6 +20,8 @@ Column { // The delay in ms to wait until updating the effect readonly property int updateDelay: 100 + readonly property int previewMargin: 5 + // Create a dummy parent to host the effect qml object function createNewComponent() { // If we have a working effect, do not show preview image as it shows through @@ -88,7 +90,9 @@ Column { tooltip: qsTr("Zoom In") onClicked: { + sourceImage.enableAnim(true) sourceImage.scale += .2 + sourceImage.enableAnim(false) } } @@ -99,18 +103,21 @@ Column { tooltip: qsTr("Zoom out") onClicked: { + sourceImage.enableAnim(true) sourceImage.scale -= .2 + sourceImage.enableAnim(false) } } HelperWidgets.AbstractButton { - enabled: sourceImage.scale !== 1 + enabled: sourceImage.scale !== 1 || sourceImage.x !== root.previewMargin + || sourceImage.y !== root.previewMargin style: StudioTheme.Values.viewBarButtonStyle buttonIcon: StudioTheme.Constants.fitAll_medium - tooltip: qsTr("Zoom Fit") + tooltip: qsTr("Reset View") onClicked: { - sourceImage.scale = 1 + sourceImage.resetTransforms() } } } @@ -174,15 +181,131 @@ Column { layer.mipmap: true layer.smooth: true + MouseArea { + id: mouseArea + + anchors.fill: parent + acceptedButtons: Qt.LeftButton + + property real pressX: 0 + property real pressY: 0 + property bool panning: false + + onPressed: { + pressX = mouseX - sourceImage.x + pressY = mouseY - sourceImage.y + panning = true + } + + onReleased: { + panning = false + } + + onWheel: (wheel) => { + let prevScale = sourceImage.scale + + if (wheel.angleDelta.y > 0) { + if (sourceImage.scale < 2) + sourceImage.scale += .2 + } else { + if (sourceImage.scale > .4) + sourceImage.scale -= .2 + } + + let dScale = sourceImage.scale - prevScale + + sourceImage.x += (sourceImage.x + sourceImage.width * .5 - wheel.x) * dScale; + sourceImage.y += (sourceImage.y + sourceImage.height * .5 - wheel.y) * dScale; + + sourceImage.checkBounds() + } + + Timer { // pan timer + running: parent.panning + interval: 16 + repeat: true + + onTriggered: { + sourceImage.x = mouseArea.mouseX - mouseArea.pressX + sourceImage.y = mouseArea.mouseY - mouseArea.pressY + sourceImage.checkBounds() + } + } + } + Image { id: sourceImage - anchors.margins: 5 - anchors.fill: parent + + function checkBounds() { + let edgeMargin = 10 + // correction factor to account for an observation that edgeMargin decreases + // with increased zoom + let corrFactor = 10 * sourceImage.scale + let imgW2 = sourceImage.paintedWidth * sourceImage.scale * .5 + let imgH2 = sourceImage.paintedHeight * sourceImage.scale * .5 + let srcW2 = source.width * .5 + let srcH2 = source.height * .5 + + if (sourceImage.x < -srcW2 - imgW2 + edgeMargin + corrFactor) + sourceImage.x = -srcW2 - imgW2 + edgeMargin + corrFactor + else if (x > srcW2 + imgW2 - edgeMargin - corrFactor) + sourceImage.x = srcW2 + imgW2 - edgeMargin - corrFactor + + if (sourceImage.y < -srcH2 - imgH2 + edgeMargin + corrFactor) + sourceImage.y = -srcH2 - imgH2 + edgeMargin + corrFactor + else if (y > srcH2 + imgH2 - edgeMargin - corrFactor) + sourceImage.y = srcH2 + imgH2 - edgeMargin - corrFactor + } + + function resetTransforms() { + sourceImage.enableAnim(true) + sourceImage.scale = 1 + sourceImage.x = root.previewMargin + sourceImage.y = root.previewMargin + sourceImage.enableAnim(false) + } + + function enableAnim(flag) { + xBehavior.enabled = flag + yBehavior.enabled = flag + scaleBehavior.enabled = flag + } + + onSourceChanged: sourceImage.resetTransforms() + fillMode: Image.PreserveAspectFit + + x: root.previewMargin + y: root.previewMargin + width: parent.width - root.previewMargin * 2 + height: parent.height - root.previewMargin * 2 source: imagesComboBox.selectedImage smooth: true + Behavior on x { + id: xBehavior + + enabled: false + NumberAnimation { + duration: 200 + easing.type: Easing.OutQuad + } + } + + Behavior on y { + id: yBehavior + + enabled: false + NumberAnimation { + duration: 200 + easing.type: Easing.OutQuad + } + } + Behavior on scale { + id: scaleBehavior + + enabled: false NumberAnimation { duration: 200 easing.type: Easing.OutQuad From 7efab2c07ee544237ceb90fddc1a3c7dfd004bbb Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Fri, 2 Feb 2024 13:36:37 +0200 Subject: [PATCH 010/176] EffectComposer: Remove warnings and unused code from the model Change-Id: I8171467e70d8d716b0598b0b533f216e5e0afc25 Reviewed-by: Miikka Heikkinen Reviewed-by: Qt CI Patch Build Bot --- .../EffectComposerPreview.qml | 2 +- .../effectcomposer/effectcomposermodel.cpp | 25 ++++--------------- .../effectcomposer/effectcomposermodel.h | 4 +-- 3 files changed, 7 insertions(+), 24 deletions(-) diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerPreview.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerPreview.qml index d3170c9d275..251b429a4d0 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerPreview.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerPreview.qml @@ -33,7 +33,7 @@ Column { oldComponent.destroy(); try { const newObject = Qt.createQmlObject( - effectComposerModel.qmlComponentString, + effectComposerModel.qmlComponentString(), componentParent, "" ); diff --git a/src/plugins/effectcomposer/effectcomposermodel.cpp b/src/plugins/effectcomposer/effectcomposermodel.cpp index 1edc70c75b4..ffa63a03536 100644 --- a/src/plugins/effectcomposer/effectcomposermodel.cpp +++ b/src/plugins/effectcomposer/effectcomposermodel.cpp @@ -9,9 +9,6 @@ #include "syntaxhighlighterdata.h" #include "uniform.h" -#include - -#include #include #include @@ -255,7 +252,7 @@ void EffectComposerModel::setVertexShader(const QString &newVertexShader) m_vertexShader = newVertexShader; } -const QString &EffectComposerModel::qmlComponentString() const +QString EffectComposerModel::qmlComponentString() const { return m_qmlComponentString; } @@ -1363,13 +1360,13 @@ void EffectComposerModel::updateCustomUniforms() void EffectComposerModel::createFiles() { - if (QFileInfo(m_vertexShaderFilename).exists()) + if (QFileInfo::exists(m_vertexShaderFilename)) QFile(m_vertexShaderFilename).remove(); - if (QFileInfo(m_fragmentShaderFilename).exists()) + if (QFileInfo::exists(m_fragmentShaderFilename)) QFile(m_fragmentShaderFilename).remove(); - if (QFileInfo(m_vertexShaderPreviewFilename).exists()) + if (QFileInfo::exists(m_vertexShaderPreviewFilename)) QFile(m_vertexShaderPreviewFilename).remove(); - if (QFileInfo(m_fragmentShaderPreviewFilename).exists()) + if (QFileInfo::exists(m_fragmentShaderPreviewFilename)) QFile(m_fragmentShaderPreviewFilename).remove(); auto vertexShaderFile = QTemporaryFile(QDir::tempPath() + "/dsem_XXXXXX.vert.qsb"); @@ -1502,16 +1499,6 @@ void EffectComposerModel::setIsEnabled(bool enabled) emit isEnabledChanged(); } -// Returns name for image mipmap property. -// e.g. "myImage" -> "myImageMipmap". -QString EffectComposerModel::mipmapPropertyName(const QString &name) const -{ - QString simplifiedName = name.simplified(); - simplifiedName = simplifiedName.remove(' '); - simplifiedName += "Mipmap"; - return simplifiedName; -} - QString EffectComposerModel::getQmlImagesString(bool localFiles) { QString imagesString; @@ -1537,8 +1524,6 @@ QString EffectComposerModel::getQmlImagesString(bool localFiles) if (uniform->enableMipmap()) imagesString += " mipmap: true\n"; - else - QString mipmapProperty = mipmapPropertyName(uniform->name()); } imagesString += " visible: false\n"; diff --git a/src/plugins/effectcomposer/effectcomposermodel.h b/src/plugins/effectcomposer/effectcomposermodel.h index 69afc503ea4..e8c7a5c61db 100644 --- a/src/plugins/effectcomposer/effectcomposermodel.h +++ b/src/plugins/effectcomposer/effectcomposermodel.h @@ -47,7 +47,6 @@ 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(QString qmlComponentString READ qmlComponentString) Q_PROPERTY(QString currentComposition READ currentComposition WRITE setCurrentComposition NOTIFY currentCompositionChanged) public: @@ -83,7 +82,7 @@ public: QString vertexShader() const; void setVertexShader(const QString &newVertexShader); - const QString &qmlComponentString() const; + Q_INVOKABLE QString qmlComponentString() const; Q_INVOKABLE void updateQmlComponent(); @@ -170,7 +169,6 @@ private: void bakeShaders(); void saveResources(const QString &name); - QString mipmapPropertyName(const QString &name) const; QString getQmlImagesString(bool localFiles); QString getQmlComponentString(bool localFiles); From f985b1b09108efb5606771a447c1b5bcf7c9464a Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 2 Feb 2024 13:08:01 +0200 Subject: [PATCH 011/176] EffectComposer: Allow 'define' properties to specify control type Control type is used to determine the control presented for the property in UI. Currently only int and bool control types are supported. Also fixed the issue that changing define wouldn't update preview. This was because changing define requires rebaking shaders, which is not normally triggered on property change. Fixes: QDS-11770 Change-Id: I953d827195565f765df1a09550c4a49da9c93c29 Reviewed-by: Mahmoud Badri --- .../EffectCompositionNodeUniform.qml | 26 ++++--- .../effectcomposer/compositionnode.cpp | 4 ++ src/plugins/effectcomposer/compositionnode.h | 1 + .../effectcomposer/effectcomposermodel.cpp | 72 +++++++++++++------ .../effectcomposer/effectcomposermodel.h | 4 ++ .../effectcomposeruniformsmodel.cpp | 1 + .../effectcomposeruniformsmodel.h | 1 + src/plugins/effectcomposer/uniform.cpp | 29 +++++++- src/plugins/effectcomposer/uniform.h | 4 ++ 9 files changed, 108 insertions(+), 34 deletions(-) diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectCompositionNodeUniform.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectCompositionNodeUniform.qml index 8dc3b75c9b5..bfe0d3e3b78 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectCompositionNodeUniform.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectCompositionNodeUniform.qml @@ -17,24 +17,30 @@ Item { visible: !uniformUseCustomValue Component.onCompleted: { - if (uniformType === "int") + if (uniformType === "int") { valueLoader.source = "ValueInt.qml" - else if (uniformType === "vec2") + } else if (uniformType === "vec2") { valueLoader.source = "ValueVec2.qml" - else if (uniformType === "vec3") + } else if (uniformType === "vec3") { valueLoader.source = "ValueVec3.qml" - else if (uniformType === "vec4") + } else if (uniformType === "vec4") { valueLoader.source = "ValueVec4.qml" - else if (uniformType === "bool") + } else if (uniformType === "bool") { valueLoader.source = "ValueBool.qml" - else if (uniformType === "color") + } else if (uniformType === "color") { valueLoader.source = "ValueColor.qml" - else if (uniformType === "sampler2D") + } else if (uniformType === "sampler2D") { valueLoader.source = "ValueImage.qml" - else if (uniformType === "define") - valueLoader.source = "ValueDefine.qml" - else + } else if (uniformType === "define") { + if (uniformControlType === "int") + valueLoader.source = "ValueInt.qml" + else if (uniformControlType === "bool") + valueLoader.source = "ValueBool.qml" + else + valueLoader.source = "ValueDefine.qml" + } else { valueLoader.source = "ValueFloat.qml" + } } RowLayout { diff --git a/src/plugins/effectcomposer/compositionnode.cpp b/src/plugins/effectcomposer/compositionnode.cpp index b63258e0af3..108eb5801d8 100644 --- a/src/plugins/effectcomposer/compositionnode.cpp +++ b/src/plugins/effectcomposer/compositionnode.cpp @@ -130,6 +130,10 @@ void CompositionNode::parse(const QString &effectName, const QString &qenPath, c m_unifomrsModel.addUniform(uniform); m_uniforms.append(uniform); g_propertyData.insert(uniform->name(), uniform->value()); + if (uniform->type() == Uniform::Type::Define) { + // Changing defines requires rebaking the shaders + connect(uniform, &Uniform::uniformValueChanged, this, &CompositionNode::rebakeRequested); + } } // Seek through code to get tags diff --git a/src/plugins/effectcomposer/compositionnode.h b/src/plugins/effectcomposer/compositionnode.h index 589954ec871..b3348bb38fc 100644 --- a/src/plugins/effectcomposer/compositionnode.h +++ b/src/plugins/effectcomposer/compositionnode.h @@ -56,6 +56,7 @@ signals: void uniformsModelChanged(); void isEnabledChanged(); void isDepencyChanged(); + void rebakeRequested(); private: void parse(const QString &effectName, const QString &qenPath, const QJsonObject &json); diff --git a/src/plugins/effectcomposer/effectcomposermodel.cpp b/src/plugins/effectcomposer/effectcomposermodel.cpp index ffa63a03536..9a3b8ca0afc 100644 --- a/src/plugins/effectcomposer/effectcomposermodel.cpp +++ b/src/plugins/effectcomposer/effectcomposermodel.cpp @@ -49,6 +49,8 @@ static bool writeToFile(const QByteArray &buf, const QString &filename, FileType EffectComposerModel::EffectComposerModel(QObject *parent) : QAbstractListModel{parent} { + m_rebakeTimer.setSingleShot(true); + connect(&m_rebakeTimer, &QTimer::timeout, this, &EffectComposerModel::bakeShaders); } QHash EffectComposerModel::roleNames() const @@ -107,10 +109,7 @@ void EffectComposerModel::addNode(const QString &nodeQenPath) { beginResetModel(); auto *node = new CompositionNode({}, nodeQenPath); - connect(qobject_cast(node->uniformsModel()), - &EffectComposerUniformsModel::dataChanged, this, [this] { - setHasUnsavedChanges(true); - }); + connectCompositionNode(node); const QList requiredNodes = node->requiredNodes(); if (requiredNodes.size() > 0) { @@ -122,10 +121,7 @@ void EffectComposerModel::addNode(const QString &nodeQenPath) const QString path = EffectUtils::nodesSourcesPath() + "/common/" + requiredId + ".qen"; auto requiredNode = new CompositionNode({}, path); - connect(qobject_cast(requiredNode->uniformsModel()), - &EffectComposerUniformsModel::dataChanged, this, [this] { - setHasUnsavedChanges(true); - }); + connectCompositionNode(requiredNode); requiredNode->setRefCount(1); m_nodes.prepend(requiredNode); } @@ -167,6 +163,7 @@ void EffectComposerModel::moveNode(int fromIdx, int toIdx) void EffectComposerModel::removeNode(int idx) { beginResetModel(); + m_rebakeTimer.stop(); CompositionNode *node = m_nodes.takeAt(idx); const QStringList reqNodes = node->requiredNodes(); @@ -193,6 +190,7 @@ void EffectComposerModel::removeNode(int idx) void EffectComposerModel::clear(bool clearName) { beginResetModel(); + m_rebakeTimer.stop(); qDeleteAll(m_nodes); m_nodes.clear(); endResetModel(); @@ -418,7 +416,7 @@ void EffectComposerModel::setEffectError(const QString &errorMessage, int type, Q_EMIT effectErrorChanged(); } -QString variantAsDataString(const Uniform::Type type, const QVariant &variant) +QString variantAsDataString(const Uniform::Type type, const Uniform::Type controlType, const QVariant &variant) { QString s; switch (type) { @@ -470,7 +468,12 @@ QString variantAsDataString(const Uniform::Type type, const QVariant &variant) } case Uniform::Type::Sampler: case Uniform::Type::Define: { - s = variant.toString(); + if (controlType == Uniform::Type::Int) + s = QString::number(variant.toInt()); + else if (controlType == Uniform::Type::Bool) + s = variant.toBool() ? QString("true") : QString("false"); + else + s = variant.toString(); break; } } @@ -495,16 +498,23 @@ QJsonObject nodeToJson(const CompositionNode &node) uniformObject.insert("name", QString(uniform->name())); QString type = Uniform::stringFromType(uniform->type()); uniformObject.insert("type", type); + if (uniform->type() == Uniform::Type::Define) { + QString controlType = Uniform::stringFromType(uniform->controlType()); + if (controlType != type) + uniformObject.insert("controlType", controlType); + } if (!uniform->displayName().isEmpty()) uniformObject.insert("displayName", QString(uniform->displayName())); - QString value = variantAsDataString(uniform->type(), uniform->value()); + QString value = variantAsDataString(uniform->type(), uniform->controlType(), + uniform->value()); if (uniform->type() == Uniform::Type::Sampler) value = QFileInfo(value).fileName(); uniformObject.insert("value", value); - QString defaultValue = variantAsDataString(uniform->type(), uniform->defaultValue()); + QString defaultValue = variantAsDataString(uniform->type(), uniform->controlType(), + uniform->defaultValue()); if (uniform->type() == Uniform::Type::Sampler) { defaultValue = QFileInfo(value).fileName(); if (uniform->enableMipmap()) @@ -517,9 +527,14 @@ QJsonObject nodeToJson(const CompositionNode &node) || uniform->type() == Uniform::Type::Int || uniform->type() == Uniform::Type::Vec2 || uniform->type() == Uniform::Type::Vec3 - || uniform->type() == Uniform::Type::Vec4) { - uniformObject.insert("minValue", variantAsDataString(uniform->type(), uniform->minValue())); - uniformObject.insert("maxValue", variantAsDataString(uniform->type(), uniform->maxValue())); + || uniform->type() == Uniform::Type::Vec4 + || uniform->controlType() == Uniform::Type::Int) { + uniformObject.insert("minValue", variantAsDataString(uniform->type(), + uniform->controlType(), + uniform->minValue())); + uniformObject.insert("maxValue", variantAsDataString(uniform->type(), + uniform->controlType(), + uniform->maxValue())); } if (!uniform->customValue().isEmpty()) uniformObject.insert("customValue", uniform->customValue()); @@ -754,10 +769,7 @@ void EffectComposerModel::openComposition(const QString &path) for (const auto &nodeElement : nodesArray) { auto *node = new CompositionNode(effectName, {}, nodeElement.toObject()); - connect(qobject_cast(node->uniformsModel()), - &EffectComposerUniformsModel::dataChanged, this, [this] { - setHasUnsavedChanges(true); - }); + connectCompositionNode(node); m_nodes.append(node); const QStringList reqIds = node->requiredNodes(); for (const QString &reqId : reqIds) @@ -922,6 +934,10 @@ QString EffectComposerModel::valueAsString(const Uniform &uniform) } else if (uniform.type() == Uniform::Type::Color) { return QString("\"%1\"").arg(uniform.value().toString()); } else if (uniform.type() == Uniform::Type::Define) { + if (uniform.controlType() == Uniform::Type::Int) + return QString::number(uniform.value().toInt()); + else if (uniform.controlType() == Uniform::Type::Bool) + return uniform.value().toBool() ? QString("1") : QString("0"); return uniform.value().toString(); } else { qWarning() << QString("Unhandled const variable type: %1").arg(int(uniform.type())).toLatin1(); @@ -1020,10 +1036,8 @@ const QString EffectComposerModel::getDefineProperties() QString s; for (Uniform *uniform : uniforms) { // TODO: Check if uniform is already added. - if (uniform->type() == Uniform::Type::Define) { - QString defineValue = uniform->value().toString(); - s += QString("#define %1 %2\n").arg(uniform->name(), defineValue); - } + if (uniform->type() == Uniform::Type::Define) + s += QString("#define %1 %2\n").arg(uniform->name(), valueAsString(*uniform)); } if (!s.isEmpty()) s += '\n'; @@ -1620,6 +1634,18 @@ QString EffectComposerModel::getQmlComponentString(bool localFiles) return s; } +void EffectComposerModel::connectCompositionNode(CompositionNode *node) +{ + connect(qobject_cast(node->uniformsModel()), + &EffectComposerUniformsModel::dataChanged, this, [this] { + setHasUnsavedChanges(true); + }); + connect(node, &CompositionNode::rebakeRequested, this, [this] { + // This can come multiple times in a row in response to property changes, so let's buffer it + m_rebakeTimer.start(200); + }); +} + QString EffectComposerModel::currentComposition() const { return m_currentComposition; diff --git a/src/plugins/effectcomposer/effectcomposermodel.h b/src/plugins/effectcomposer/effectcomposermodel.h index e8c7a5c61db..c0eb76f0b94 100644 --- a/src/plugins/effectcomposer/effectcomposermodel.h +++ b/src/plugins/effectcomposer/effectcomposermodel.h @@ -12,6 +12,7 @@ #include #include #include +#include namespace ProjectExplorer { class Target; @@ -172,6 +173,8 @@ private: QString getQmlImagesString(bool localFiles); QString getQmlComponentString(bool localFiles); + void connectCompositionNode(CompositionNode *node); + QList m_nodes; int m_selectedIndex = -1; @@ -206,6 +209,7 @@ private: bool m_loadComponentImages = true; bool m_isEnabled = true; QString m_currentComposition; + QTimer m_rebakeTimer; const QRegularExpression m_spaceReg = QRegularExpression("\\s+"); }; diff --git a/src/plugins/effectcomposer/effectcomposeruniformsmodel.cpp b/src/plugins/effectcomposer/effectcomposeruniformsmodel.cpp index e547883b9dd..9d8c1a31a72 100644 --- a/src/plugins/effectcomposer/effectcomposeruniformsmodel.cpp +++ b/src/plugins/effectcomposer/effectcomposeruniformsmodel.cpp @@ -26,6 +26,7 @@ QHash EffectComposerUniformsModel::roleNames() const roles[MinValueRole] = "uniformMinValue"; roles[MaxValueRole] = "uniformMaxValue"; roles[TypeRole] = "uniformType"; + roles[ControlTypeRole] = "uniformControlType"; roles[UseCustomValueRole] = "uniformUseCustomValue"; return roles; } diff --git a/src/plugins/effectcomposer/effectcomposeruniformsmodel.h b/src/plugins/effectcomposer/effectcomposeruniformsmodel.h index 3e2d44c6260..f60c016ed09 100644 --- a/src/plugins/effectcomposer/effectcomposeruniformsmodel.h +++ b/src/plugins/effectcomposer/effectcomposeruniformsmodel.h @@ -37,6 +37,7 @@ private: MaxValueRole, MinValueRole, TypeRole, + ControlTypeRole, UseCustomValueRole }; diff --git a/src/plugins/effectcomposer/uniform.cpp b/src/plugins/effectcomposer/uniform.cpp index e8fd77d631f..4c8b994388e 100644 --- a/src/plugins/effectcomposer/uniform.cpp +++ b/src/plugins/effectcomposer/uniform.cpp @@ -22,6 +22,7 @@ Uniform::Uniform(const QString &effectName, const QJsonObject &propObj, const QS m_name = propObj.value("name").toString(); m_description = propObj.value("description").toString(); m_type = Uniform::typeFromString(propObj.value("type").toString()); + m_controlType = m_type; defaultValue = propObj.value("defaultValue").toString(); m_displayName = propObj.value("displayName").toString(); @@ -44,6 +45,12 @@ Uniform::Uniform(const QString &effectName, const QJsonObject &propObj, const QS g_propertyData[mipmapProperty] = m_enableMipmap; } + if (m_type == Type::Define) { + QString controlType = propObj.value("controlType").toString(); + if (!controlType.isEmpty()) + m_controlType = Uniform::typeFromString(controlType); + } + m_customValue = propObj.value("customValue").toString(); m_useCustomValue = getBoolValue(propObj.value("useCustomValue"), false); @@ -61,12 +68,22 @@ Uniform::Type Uniform::type() const return m_type; } +Uniform::Type Uniform::controlType() const +{ + return m_controlType; +} + // String representation of the type for qml QString Uniform::typeName() const { return Uniform::stringFromType(m_type); } +QString Uniform::controlTypeName() const +{ + return Uniform::stringFromType(m_controlType); +} + QVariant Uniform::value() const { return m_value; @@ -216,6 +233,13 @@ QVariant Uniform::getInitializedVariant(bool maxValue) return maxValue ? QVector4D(1.0, 1.0, 1.0, 1.0) : QVector4D(0.0, 0.0, 0.0, 0.0); case Uniform::Type::Color: return maxValue ? QColor::fromRgbF(1.0f, 1.0f, 1.0f, 1.0f) : QColor::fromRgbF(0.0f, 0.0f, 0.0f, 0.0f); + case Uniform::Type::Define: + if (m_controlType == Uniform::Type::Bool) + return maxValue ? true : false; + else if (m_controlType == Uniform::Type::Int) + return maxValue ? 100 : 0; + else + return QVariant(); default: return QVariant(); } @@ -262,7 +286,10 @@ QVariant Uniform::valueStringToVariant(const QString &value) variant = value; break; case Uniform::Type::Define: - variant = value; + if (m_controlType == Uniform::Type::Bool) + variant = (value == "true"); + else + variant = value; break; } diff --git a/src/plugins/effectcomposer/uniform.h b/src/plugins/effectcomposer/uniform.h index 83f15224aec..09081248c54 100644 --- a/src/plugins/effectcomposer/uniform.h +++ b/src/plugins/effectcomposer/uniform.h @@ -20,6 +20,7 @@ class Uniform : public QObject Q_PROPERTY(QString uniformName MEMBER m_displayName CONSTANT) Q_PROPERTY(QString uniformType READ typeName CONSTANT) + Q_PROPERTY(QString uniformControlType READ controlTypeName CONSTANT) Q_PROPERTY(QString uniformDescription READ description CONSTANT) Q_PROPERTY(QVariant uniformValue READ value WRITE setValue NOTIFY uniformValueChanged) Q_PROPERTY(QVariant uniformBackendValue READ backendValue NOTIFY uniformBackendValueChanged) @@ -45,7 +46,9 @@ public: Uniform(const QString &effectName, const QJsonObject &props, const QString &qenPath); Type type() const; + Type controlType() const; QString typeName() const; + QString controlTypeName() const; QVariant value() const; void setValue(const QVariant &newValue); @@ -90,6 +93,7 @@ private: QString m_qenPath; Type m_type; + Type m_controlType; QVariant m_value; QVariant m_defaultValue; QVariant m_minValue; From 85f32353a974091a3198d5b847479e1d95e5cf53 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Fri, 2 Feb 2024 14:20:37 +0200 Subject: [PATCH 012/176] EffectComposer: Fix pan/zoom not working after adding a node Also increase max zoom level to 3 instead of 2 Change-Id: Ia642bc65c0f902a0aff8fd81fc6f623981f2b249 Reviewed-by: Miikka Heikkinen --- .../EffectComposerPreview.qml | 106 +++++++++--------- 1 file changed, 53 insertions(+), 53 deletions(-) diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerPreview.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerPreview.qml index 251b429a4d0..892132a39fa 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerPreview.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerPreview.qml @@ -84,7 +84,7 @@ Column { anchors.verticalCenter: parent.verticalCenter HelperWidgets.AbstractButton { - enabled: sourceImage.scale < 2 + enabled: sourceImage.scale < 3 style: StudioTheme.Values.viewBarButtonStyle buttonIcon: StudioTheme.Constants.zoomIn_medium tooltip: qsTr("Zoom In") @@ -174,6 +174,58 @@ Column { height: root.height - y clip: true + MouseArea { + id: mouseArea + + anchors.fill: parent + acceptedButtons: Qt.LeftButton + + property real pressX: 0 + property real pressY: 0 + property bool panning: false + + onPressed: { + pressX = mouseX - sourceImage.x + pressY = mouseY - sourceImage.y + panning = true + } + + onReleased: { + panning = false + } + + onWheel: (wheel) => { + let prevScale = sourceImage.scale + + if (wheel.angleDelta.y > 0) { + if (sourceImage.scale < 3) + sourceImage.scale += .2 + } else { + if (sourceImage.scale > .4) + sourceImage.scale -= .2 + } + + let dScale = sourceImage.scale - prevScale + + sourceImage.x += (sourceImage.x + sourceImage.width * .5 - wheel.x) * dScale; + sourceImage.y += (sourceImage.y + sourceImage.height * .5 - wheel.y) * dScale; + + sourceImage.checkBounds() + } + + Timer { // pan timer + running: parent.panning + interval: 16 + repeat: true + + onTriggered: { + sourceImage.x = mouseArea.mouseX - mouseArea.pressX + sourceImage.y = mouseArea.mouseY - mouseArea.pressY + sourceImage.checkBounds() + } + } + } + Item { // Source item as a canvas (render target) for effect id: source anchors.fill: parent @@ -181,58 +233,6 @@ Column { layer.mipmap: true layer.smooth: true - MouseArea { - id: mouseArea - - anchors.fill: parent - acceptedButtons: Qt.LeftButton - - property real pressX: 0 - property real pressY: 0 - property bool panning: false - - onPressed: { - pressX = mouseX - sourceImage.x - pressY = mouseY - sourceImage.y - panning = true - } - - onReleased: { - panning = false - } - - onWheel: (wheel) => { - let prevScale = sourceImage.scale - - if (wheel.angleDelta.y > 0) { - if (sourceImage.scale < 2) - sourceImage.scale += .2 - } else { - if (sourceImage.scale > .4) - sourceImage.scale -= .2 - } - - let dScale = sourceImage.scale - prevScale - - sourceImage.x += (sourceImage.x + sourceImage.width * .5 - wheel.x) * dScale; - sourceImage.y += (sourceImage.y + sourceImage.height * .5 - wheel.y) * dScale; - - sourceImage.checkBounds() - } - - Timer { // pan timer - running: parent.panning - interval: 16 - repeat: true - - onTriggered: { - sourceImage.x = mouseArea.mouseX - mouseArea.pressX - sourceImage.y = mouseArea.mouseY - mouseArea.pressY - sourceImage.checkBounds() - } - } - } - Image { id: sourceImage From 6ff78b73cecec23135a5522adf8f84ff3557f22b Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Fri, 2 Feb 2024 14:51:21 +0200 Subject: [PATCH 013/176] EffectComposer: add zoom indicator Change-Id: Iaedd1ec9d6f7ee169a3b69fff4a33051786653bf Reviewed-by: Miikka Heikkinen --- .../EffectComposerPreview.qml | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerPreview.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerPreview.qml index 892132a39fa..50c8e32c715 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerPreview.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerPreview.qml @@ -93,6 +93,7 @@ Column { sourceImage.enableAnim(true) sourceImage.scale += .2 sourceImage.enableAnim(false) + zoomIndicator.show() } } @@ -106,6 +107,7 @@ Column { sourceImage.enableAnim(true) sourceImage.scale -= .2 sourceImage.enableAnim(false) + zoomIndicator.show() } } @@ -211,6 +213,7 @@ Column { sourceImage.y += (sourceImage.y + sourceImage.height * .5 - wheel.y) * dScale; sourceImage.checkBounds() + zoomIndicator.show() } Timer { // pan timer @@ -332,6 +335,32 @@ Column { layer.smooth: true } + Rectangle { + id: zoomIndicator + + width: 40 + height: 20 + color: StudioTheme.Values.themeDialogBackground + visible: false + + function show() { + zoomIndicator.visible = true + zoomIndicatorTimer.start() + } + + Text { + text: Math.round(sourceImage.scale * 100) + "%" + color: StudioTheme.Values.themeTextColor + anchors.centerIn: parent + } + + Timer { + id: zoomIndicatorTimer + interval: 1000 + onTriggered: zoomIndicator.visible = false + } + } + Connections { target: effectComposerModel function onShadersBaked() { From 916b78aa49806aa79c33446640656268eab468f9 Mon Sep 17 00:00:00 2001 From: Brook Cronin Date: Fri, 2 Feb 2024 13:58:38 +0100 Subject: [PATCH 014/176] Qml Designer: fix start and end angle tooltips Change-Id: Ic854ae254512e5f1789d558bed02f7a020558ffd Reviewed-by: Thomas Hartmann Reviewed-by: Pranta Ghosh Dastider --- .../QtQuick/Controls/DialSpecifics.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/DialSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/DialSpecifics.qml index 92ab3a42ff6..8f3abb2ed19 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/DialSpecifics.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/DialSpecifics.qml @@ -106,7 +106,7 @@ Column { PropertyLabel { text: qsTr("Start angle") - tooltip: qsTr("This property holds the starting angle of the dial in degrees.") + tooltip: qsTr("Sets the starting angle of the dial in degrees.") visible: majorQtQuickVersion === 6 && minorQtQuickVersion >= 6 } @@ -128,7 +128,7 @@ Column { PropertyLabel { text: qsTr("End angle") - tooltip: qsTr("This property holds the ending angle of the dial in degrees.") + tooltip: qsTr("Sets the ending angle of the dial in degrees.") visible: majorQtQuickVersion === 6 && minorQtQuickVersion >= 6 } From d83eeb0b8fcffe1c2e5ee0848a723b7e0c71e687 Mon Sep 17 00:00:00 2001 From: Brook Cronin Date: Fri, 2 Feb 2024 13:44:42 +0100 Subject: [PATCH 015/176] Qml Designer: Add uniform cell size properties to layouts Change-Id: If2e0958c124cff259b1dbea57dd798711f0a0f1d Reviewed-by: Pranta Ghosh Dastider Reviewed-by: Thomas Hartmann --- .../QtQuick/Layouts/ColumnLayoutSpecifics.qml | 17 +++++++++++ .../QtQuick/Layouts/GridLayoutSpecifics.qml | 29 +++++++++++++++++++ .../QtQuick/Layouts/RowLayoutSpecifics.qml | 17 +++++++++++ 3 files changed, 63 insertions(+) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Layouts/ColumnLayoutSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Layouts/ColumnLayoutSpecifics.qml index 7cdb040956c..2072f13a8e3 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Layouts/ColumnLayoutSpecifics.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Layouts/ColumnLayoutSpecifics.qml @@ -44,5 +44,22 @@ Section { ExpandingSpacer {} } + + PropertyLabel { + text: qsTr("Uniform cell size") + tooltip: qsTr("Toggles all cells to have a uniform size.") + visible: majorQtQuickVersion === 6 && minorQtQuickVersion >= 6 + } + + SecondColumnLayout { + CheckBox { + implicitWidth: StudioTheme.Values.twoControlColumnWidth + + StudioTheme.Values.actionIndicatorWidth + backendValue: backendValues.uniformCellSizes + visible: majorQtQuickVersion === 6 && minorQtQuickVersion >= 6 + } + + ExpandingSpacer {} + } } } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Layouts/GridLayoutSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Layouts/GridLayoutSpecifics.qml index 37437693534..8eea1435422 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Layouts/GridLayoutSpecifics.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Layouts/GridLayoutSpecifics.qml @@ -113,5 +113,34 @@ Section { ExpandingSpacer {} } + + PropertyLabel { + text: qsTr("Uniform cell sizes") + tooltip: qsTr("Toggles all cells to have a uniform height or width.") + visible: majorQtQuickVersion === 6 && minorQtQuickVersion >= 6 + } + + SecondColumnLayout { + CheckBox { + text: qsTr("Heights") + implicitWidth: StudioTheme.Values.twoControlColumnWidth + + StudioTheme.Values.actionIndicatorWidth + backendValue: backendValues.uniformCellHeights + visible: majorQtQuickVersion === 6 && minorQtQuickVersion >= 6 + } + + Spacer { implicitWidth: StudioTheme.Values.twoControlColumnGap } + + CheckBox { + text: qsTr("Widths") + implicitWidth: StudioTheme.Values.twoControlColumnWidth + + StudioTheme.Values.actionIndicatorWidth + backendValue: backendValues.uniformCellWidths + visible: majorQtQuickVersion === 6 && minorQtQuickVersion >= 6 + } + + ExpandingSpacer {} + } } } + diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Layouts/RowLayoutSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Layouts/RowLayoutSpecifics.qml index c1abe023a49..b4a2ced1cdd 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Layouts/RowLayoutSpecifics.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Layouts/RowLayoutSpecifics.qml @@ -44,5 +44,22 @@ Section { ExpandingSpacer {} } + + PropertyLabel { + text: qsTr("Uniform cell size") + tooltip: qsTr("Toggles all cells to have a uniform size.") + visible: majorQtQuickVersion === 6 && minorQtQuickVersion >= 6 + } + SecondColumnLayout { + CheckBox { + implicitWidth: StudioTheme.Values.twoControlColumnWidth + + StudioTheme.Values.actionIndicatorWidth + backendValue: backendValues.uniformCellSizes + visible: majorQtQuickVersion === 6 && minorQtQuickVersion >= 6 + } + + ExpandingSpacer {} + } } } + From 17f12979a6bf4ee7bdb24ede84f524157dfabea4 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 2 Feb 2024 16:49:02 +0200 Subject: [PATCH 016/176] QmlDesigner: Add extended View3D component library icons Fixes: QDS-11811 Change-Id: I0d33311e68b302a531d3e3273d10e6f4e3ff0932 Reviewed-by: Mahmoud Badri --- .../qtquickplugin/images/extended-view3d-16px.png | Bin 0 -> 254 bytes .../qtquickplugin/images/extended-view3d-24px.png | Bin 0 -> 305 bytes .../images/extended-view3d-24px@2x.png | Bin 0 -> 455 bytes .../qmldesigner/qtquickplugin/qtquickplugin.qrc | 3 +++ .../qmldesigner/qtquickplugin/quick.metainfo | 4 ++-- 5 files changed, 5 insertions(+), 2 deletions(-) create mode 100644 src/plugins/qmldesigner/qtquickplugin/images/extended-view3d-16px.png create mode 100644 src/plugins/qmldesigner/qtquickplugin/images/extended-view3d-24px.png create mode 100644 src/plugins/qmldesigner/qtquickplugin/images/extended-view3d-24px@2x.png diff --git a/src/plugins/qmldesigner/qtquickplugin/images/extended-view3d-16px.png b/src/plugins/qmldesigner/qtquickplugin/images/extended-view3d-16px.png new file mode 100644 index 0000000000000000000000000000000000000000..81e4de13679e6973e3e77a866eeaa3b044e9fa52 GIT binary patch literal 254 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4rT@h1`S>QUuzI^$L6)To6U%qnX$`z>Khu^h7AhSw>{DK(-6bu{^3hEo?FId0- z{QdWdQ`bo{Ffe3!x;TbNgqNOh6gp(U!*bB^#1X$1^!1;K3CW>2w~)lXZ+vBX-)9pn)PPgg&ebxsLQ07iFZH~;_u literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/qtquickplugin/images/extended-view3d-24px.png b/src/plugins/qmldesigner/qtquickplugin/images/extended-view3d-24px.png new file mode 100644 index 0000000000000000000000000000000000000000..70cd99bfe15d2557d749f64ce2b9182ada420d6e GIT binary patch literal 305 zcmeAS@N?(olHy`uVBq!ia0y~yV2}V|4rT@hhU+WOo?>8NFb?nuab;j&SigS#^5x4{ zty;Al3|6dIv2x|g<;$0^T)A=u7%X4D985!r)T>g-3=9kcB|(0{3<3%U4haSI4gC}5 zFW7(o{r}mEQW+Q+=6bp~hG+!Oz39kySb@joqCESgEt+ZX>Tl`wgr3;6dBX)82PXyw z*X%zG2b`BjusdXPM;^TsxBcvcLd_D-^htHa{k-W;k^zsFJTY}>IGoX}lUVDnoqFKh zw(TqPq!eCmdlh(F*H_?4sr7C-1^cs$>KaO7cm9&L^RM7P(8Ok<%(zB%3zM)zVQly{ uV~4e-uSKVI{QG@Q^O?*~NAF)BSyOHZ{@&C2qMw0*fx*+&&t;ucLK6VoXM)B6 literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/qtquickplugin/images/extended-view3d-24px@2x.png b/src/plugins/qmldesigner/qtquickplugin/images/extended-view3d-24px@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..c6ea236a02e4153bf936ebc9545e622408124446 GIT binary patch literal 455 zcmeAS@N?(olHy`uVBq!ia0y~yU@!n-4rT@hhJ-tuTNxM_ECYN(Tp1V`)~{c`eEITK zt5z)sgH@|mtyr;Q<;s=ImoHzra^(szSiXEYm_`r_UmQNiz`!6>666=mAfRC2kdR+c z-_So{{(}AI@4r865FgCIz^Lu%;uzv_{O#3?Rm}=KtPdtkyjsF{xblDdUDv5iY*)J9 zX5HC(|L0m>MS->4;nRGh@4UG5$aVYkcxI7Lz5z}9x!%M!zlxo2eE&zQK`MjrZU*)b zUFMx zVT)|GpWbnmv9yN24vN|ycO;MTdP;k2w0?J6ly_C!9`+6wm9lkfPG5g` tzftDhlbasm`{wa?Z<8&l^3?dtAU54cdapo_00RR9gQu&X%Q~loCIH&>$nyXI literal 0 HcmV?d00001 diff --git a/src/plugins/qmldesigner/qtquickplugin/qtquickplugin.qrc b/src/plugins/qmldesigner/qtquickplugin/qtquickplugin.qrc index e71d6875554..34838797d49 100644 --- a/src/plugins/qmldesigner/qtquickplugin/qtquickplugin.qrc +++ b/src/plugins/qmldesigner/qtquickplugin/qtquickplugin.qrc @@ -124,5 +124,8 @@ images/drop-area-24px.png images/drop-area-24px@2x.png images/animatedsprite-loading.png + images/extended-view3d-16px.png + images/extended-view3d-24px.png + images/extended-view3d-24px@2x.png diff --git a/src/plugins/qmldesigner/qtquickplugin/quick.metainfo b/src/plugins/qmldesigner/qtquickplugin/quick.metainfo index e0ea712ebdc..3f3cdb7910c 100644 --- a/src/plugins/qmldesigner/qtquickplugin/quick.metainfo +++ b/src/plugins/qmldesigner/qtquickplugin/quick.metainfo @@ -853,12 +853,12 @@ MetaInfo { Type { name: "QtQuick3D.View3D" - icon: ":/qtquickplugin/images/default3d16.png" + icon: ":/qtquickplugin/images/extended-view3d-16px.png" ItemLibraryEntry { name: "Extended View3D" category: "Items" - libraryIcon: ":/qtquickplugin/images/default3d.png" + libraryIcon: ":/qtquickplugin/images/extended-view3d-24px.png" version: "6.5" requiredImport: "QtQuick3D" QmlSource { source: ":/qtquickplugin/source/extendedview3D_template.qml" } From 9201491292b616f62387c1b4dc319a84e602e9ff Mon Sep 17 00:00:00 2001 From: Teea Poldsam Date: Mon, 5 Feb 2024 10:02:30 +0200 Subject: [PATCH 017/176] Doc: Update the image for 3D models Updated the image for 3D models and added "sphere" in description text. Task-number: QDS-11888 Change-Id: Iab917dea2baff548140751a095325494791a8bdf Reviewed-by: Reviewed-by: Mats Honkamaa Reviewed-by: Qt CI Patch Build Bot --- doc/qtdesignstudio/images/studio-3d-models.png | Bin 24452 -> 0 bytes doc/qtdesignstudio/images/studio-3d-models.webp | Bin 0 -> 26110 bytes .../qtdesignstudio-3d-model.qdoc | 4 ++-- 3 files changed, 2 insertions(+), 2 deletions(-) delete mode 100644 doc/qtdesignstudio/images/studio-3d-models.png create mode 100644 doc/qtdesignstudio/images/studio-3d-models.webp diff --git a/doc/qtdesignstudio/images/studio-3d-models.png b/doc/qtdesignstudio/images/studio-3d-models.png deleted file mode 100644 index de4f65d5e8a16bcebbdfd2437494fe7a31160855..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24452 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 diff --git a/doc/qtdesignstudio/images/studio-3d-models.webp b/doc/qtdesignstudio/images/studio-3d-models.webp new file mode 100644 index 0000000000000000000000000000000000000000..cb13aed64d9aedf03b88bf4520faf8c7db313aa0 GIT binary patch literal 26110 zcmWIYbaVTb%D@or>J$(bV4?6Tm4V?y5Yt>ntvKdijCOO~=J~8rU%q(JM(q+l)9wttQ-U;4EFTmR3UvIX^Tt#3ZD_x?Bg_xiW;U;a(K_w?iW<9c>=`senQ z?|<^^;O~9kIsX>?FMqlCwea`sE9P(F-z?u_{#*X`{<8YJdmZdg)Ng+s{YCAc|G&qV zXMfLr*!_F@U;aD&S^ocjK9hg@|Ix3eU;3Z#f7>(lP2zSp`G4kr&OOz9^01#)Bfz)^y}_B=S@F+L#KQ0BJJ(PT8B4valLdaS;{wQ zL)`?Up8u+El|5%3%{l7y>(#9On%587(_ZtgeYs}F<%)vSIvX;Nh~E5kTPcNma_ZOL z;f6nU-;KSw>-Zv1?P8^_XEzP5Bqo+VXzM*ASpNJ}Q`-I0f;THll-1u|v6=Ct<(p{B zr|Y5GX~wB9(+i3vy@W1x)IPiTqTx@)4dIzR{O8pSXGa9wQwnfX|E~1((UtmjlT2R- zKD|0wMEgQYR}JItd2e)VwipE1m~+KdrmJPVGnB2G`|DgU&+H?A8DGhriO7BS|J5x~ zxA#S=Ykst>J659-`ud&h5{Be%FRS~0m$xQ`r!%*uE@Uo_+@Z`=q_ETb>ms42v0GLx ziT6JD^xtBkZGDTk8+vbAByr}VId}AvKSzwpw7A;={RvMuC1y!l8 z!}JTeAf>Li46j7VlE z$ew;Kmup>Blh;25Npk0@GOnECzOyuIE#DTi=lLqt>T!;io4ytoyB@l%y>x}pFCN~m z7wrqq8<+*DEuO-p)0o|DA=YjZwSzD9$YH>_Xew(LxkSU-RBm%UTp zw_B}zCwb2AQ^wC#He&LdGE4$=9mLm*+i+Xocdl_aniK5#q0GQ)PFTyd_ZCY6bUm*z z#xw5S(f{}ElZa{CpWSY0Nz{vMQ-8|&G5oH|nRoYB*`M5@8NDg)k*wn$?|G|5I8~*U zHuLM|m+Hko*#0cG_~2QuqmrQwTh*hLmW3amb4~DfzaU%x8?9+xdvs#x6XFb!Om=XBz|5W2c78gWvx9BdYpPkNs|9#EARE5VT-p{xmxY%CY z>GSt@5c^8^laawmpW933?O4BveZ`s7*z~;tY+CbQTkkI4a7I{>_0Z8fa~)qPua&9$ ze&HQ!{oFExSL*v2f@1!?y>G70pQ_;)cB^Su$$OzClTD@mJip`c{@+%Cw35}^=PCU4 ztNm~)>%Pp%*GuBvHmWP#Httq4mR=Y>iA~Xa(yH|OPYGgIeU?t%mHA>v*FKro_k7iZ z7vJ@ry2$6u`QSye`x-C#l*P{7{!QY=cMeO3b9-lSyxq0CTCV+bv+Y;GYVTF2Y7-mZ z#@R-^e{9zz%IzNh{$P67A?^afnQ2-V{L;O*^%mu5EzW$n@Hm^k&?M<+^Y&inU)TAc zn`xJZOu&NXtX3!S908$PyW$zI{kOB2D}}{xJTrHDoxHhIL!d%k|MiDczq~IgThcYR z!$h(<@awmS>;5R+?Qo9E@qDzb>-wbYQzpsY7q-1xR?qd|@SmMwj#{6)4&8UPxVP8m z(Y7P!(&uz2eBWmpWBEtw%n{2eg|`l^o#{9sT)}Ej%F2}V!gn(byi0DL|F!I8%%qPG z+TAxLTNW*r+h?MnoXTQwI{f#+?1&`|(+_1yoqW0YxY)Cp6{@ca7KLv9S2szsmFKO^ zhqv+krq%zSmQReYxhZvenC0WRAXxdx-v0N` z`Hy8VS28Tt|NcqyO!|YX54aNZ#LeX{pVu{5badfi!Rl8R<^J68%$gEtF?+_$+~jwx z&1ZF;bj3DS$+c|y?{QWuF>lhZWhR~y7iG`-F|kT?UL4xm%so0*>*JNh3~U)=<4DNf$L%no@d_TTa&TOyi`Rd zc>cpDPc;%}n+Gnq9v{$p<;lepKQq67ygN^MxAH~#A1d;{uS zebO@3GpcUFe{V=ynD=e|U9bH2{xs`mZH?#)r=CkqyYOPscY7^=pQ5b8+a%VSZOT%= zJIC94;WQtGoR$q6uOCZYuInhjNcw1~E76@jUMxPi})8Z<=lTH#d3ZB z@g)-iYoo5UN_^h_`0y;}e@AsbENk9p@ap5LYOQ`NzFCnqcUK3!pQg0_>XR=4Wd@TY zS;`(=D^SvwlQV67Z!Z7*st!w_o6-VBjVR5#nj#mu{UgtCMXdh2Wz~W%r6n_W)s#&v zcDTRgkVgL+kqs{cQ;)vc;vxIQM$S3NC2^PFtcNF@logbx_N~lXCbYgxf@NEfQ)zsa zx8F>=9aq;`l^j!2-&xe&;gM^>RbsStkEET`fAK{-18@635V&Oau_%3a{WE3HHCb^t zr`|r1b0RZ&(s73SzR_8CK03Tzuwb{(A3KwVNq*DgIE_0Qe$Tu)VcGWMkyBXYnm$gS z5c|peFwd8&*=6lfFZMC#Ia*sW9Cf@m@9DDum(Lnz@e{X(%P6GEHN=E{ZkjAn*tzLi zqQ#pIyR!e^|JlmExPQ3SVA_qKVoQ6kdD0TAFT^|#jk&<)w|&OD`@FB4#0p)HP5=4Y zf5-0ia+da+Z(FW4`tS8&QMk)5**&jT-M-+`uO-^0%_*kwk>4(2+ro$=3ce>6DwlVk z|G8cvSLboS1T+26SNLlQ?6ne(wJqR9$i=F zEZ*>X$?^X7W$U&jee>6O7CW(&-Q)Gu-r$9i<*Ie2Q`KeS*6+>?*5vj$xopD@Z|{H8 z3irobmj{Mi|2`$<@cNPunx+l&?UH3G^SSDp7@|0?SDf4NYsIOPQ(dA{{(swB(0~`H!0=vw%BC5Q z3xd~NO{z^fvu*L}pN}p-H=3a`L-qId>TMfjpH-!&_xr{5{qvwY4y zKGpQ=$CY2*jye*&!Oj0`)`jJ(%si7n%=&lVdCISf(kHL>KVPGLXhYfUN$=vECCYtx z_lGzH2`hj36mI)eKhWirKHr4TTP*VBOa0Vc)|s5)yz=R;;?4a{&U$An6a;2pTe<6y z=0hFHeW!k`zQ}yVI&|j!43%pxcDuLdtcuOMfBw#bWhU!HmMEi-#xFyUR9C zn^-9|U%p1+eoa^GjemziURMd}%&fOfe)X>OHRtA|E2jIbGplMe@n_(^Ti0)8u=M6? zmfdBw&c~cBZ}6LzwU?FsGfP@2&=u?EWux&UW?8~_&O7$&X55eey>Qu+I)*2AHkdAc zzeA5^O@we#59&EE1X zrc>h28Yljfl?bEScY9Wmzf;X)3I|a$JJ+B&dcU(sN$G6 z@r)h+EUU=5Jniif(m5S$uPTx(uOC_#SQDj{RB>rR&K30>yQO5EBs^|U_E{=a$+qea zM~tR;ko*D>9tJT})m^CE0d>Xkp~TkJPv%;M15+3+rK>Wd{`x0&9OjLkYM zJGtz~{D;c7YBp#-`}%*MD)&{6H;e~-uKToY>M7z4mHl_paF6V?doyiX=DvE7%pq;M z*{mX>>#5?F+mG^-(goGi;=fy(hVtH+{pY>&c6%1-w-xrA*D$Wxxc_bErQ^a12}^kO-mQsvS&|_h=jP8_ka7Hch?!<+&!l7i2h&Pj zcdUtQf9qSm(o^|R=Hl)*zi!W{u1@0lZ2XXQH;2Ohra8Xuhqsi)tUTu|`8Ub>+bb3S zcXMv6SW_!L$<8D?&R@JmHSNllA6-4qHe~l74gJ+&#`bdWZ~1G#wldDxqp@f0wLj~M z-iL?&U3cx5Z_FZR&vuJ9Y>Q^sGoG;*yRuk$(}#b0O9UL_--KknopxoZ&^43lOCtTt zt$!}N6=J=Wr6cys{+?TM;#`Y4xtSh*V7aHr@pO&8tO3u%`+losKbU_@Gur94G;iK9 zA*r-45gDG6dDAKyMNYkJY}IRGeCHkg$1}0~M6czOs-v7cf4!__c8yw~S{Uxb{P1Q^ z2kXwoH!gbHM!jH(4k?=VqvGa$iQm!T#p*YAoyfg>T+kwV<5T0M@5N5mZ`a=THZRjw zc2B*-DR-WSJ0#bK$ryb9xp@1-X|qigl-T|}3JK$KRhF&T6<4IX^u$K{w}(ZQpR5$13_meS`4&S@C`CwSygIrUF93_=xW%~;)-~M_W zshi^B{NU%l$bW(_E2X~_ecNp0aOT3F<`deM3&XB_`?{55@+IdFFDC?LI68J@+_Lk3 zceP3nVC?OCV2 zne5liJ+VoC?&lr-x3-pVF!jD4efwR6;vH$LAJ&hUrP;1AzbihxM}JGmjGw0ym+Bha zsjB)nP2#Lhv%{@l&K(i9_2S9LbL!*OcP{&M`21sA#`o*e|C(Qo+00e5tE$%InU>Gl zs#poRgEQBAFJ``BZ2Gb!p_(miuU-#-!kOz!FE+lBob_dwfo-#K+;lhg4QbhzUL5$w zgH6kkOKkHC=6U*(rmq;+DE$gP>v*Lk zsk@|t$?)_l=8|rI#W|0%z4oM^O;WA3`hWh-{V?Ha8DA7y0%q{WtrFkjEY14x`|}Bp zQY!5V8QMKpNU=@a?Dfp*BEOkcpG$e^w@2UY&aPYdd6Sd&vbT9w-|tP$JsdBWFv)F| z)tC8B$?i4mU6l>zHm=<8`E1wC(OnHg^t z{LkOS-|Y#!Y4S=jcAZ^Tg0N|Ai^JUmT@NjAyG= zrfKc{E#rRn;x*gX)Z8~&M@{9VDWe&O*o`tG?{rbXJlcm5s=tWI#xZ#vp9$dy} zZIq7k-sZpm{@2$J%^6mE4_~?$W~idm@TleDG5aGD|Bv;DMrY4-_%WgSd5~FFn$aa? zX|HukZ~EnR)r5CmnRskz6sv~C+s-SK_Fiy0#2C0UYpD>c-nO$RSmu|QeX5Fn-!`{R zwNrd)_Rb}{#eXi=`}6O6)y~t=Ws{0?3#-DJPVyODN|OD~^49FjMFSo&sKL$ z%B|P1Y5V`f^3ClP9BYovouPl_kJ}>Mx$DEM4HJ95edu^??tiZ2^--x=r`Mddj}hS7nYQYUn+GdR8zX?6}92nBJI8IU4q9PSTehcw{%Stag3q(v=sG{By3* zvg9XsYIm)4n8Pdl_(|7-HMwOId8$p`3tzBYue>_(*Zjv5tt_pB=HEPfNWS|y!<(dV zo#!`mB5VY9ea&j~(&}F`>yw($vW<;x7yf?QS?iGdQ*KSj&z1k@`MvCu`(xA-ZF_%P zhhngN(%jp8Gd7=8E&bA{zxjMyM!nvRIjYxw|N5^suW@bnxkHRj7bf}dfBNjm`v2|z z;&OZZmut3uFSQEHT%>xruD$);o+$z*GW+ZEZA<>nySG^DR~Ng~Ws6gXM8Bq$zOu5L z{r4kl)0W4o9 zs?D2MMC@%WE?J&@r)hQyY__7RcZFw=dDtM>Q*i|8hkN$*0VcRyvuWSG*0P> z?z$ScOI1Mn%0rL)7pmdML({mTJplU*^}Br+MG_**|NtU2id;V>Q)irLmgTv>9KvhTPx^D%;YM zdp&JwxYv$ud3LuKxn&Arspn1fZ&u8gT;aW0)gigvgYA^ow!h5`V%oQVHf%Ymwf0BI zx~}u_i}cr+uFqOfDjW4RZv6*_tYST*k9F&hy!QWWw)gnA&F8eMrrhzIZIYM%Ugy=* z-PxPI^DfzLv2#QA{A)d}`qPUe7+24G`uJ96;JJf$e?2z6n=I77S-0a`YT&v2o%QFl zyr!+4eQ9Qa!C_|JLfb><+>VFtOmlx|lN$W;^N|}XCB(#fb-VOslI|XG_`XiUbJ5iY zo2LC)x|E$ix^umW+3V_4ljg6edL(`Sm#)Zhx%u_~?Dou3{O8;KXI4a z?k&06<9Xrz$76=93zqDjYpLw{%r*Rf_FS+A+#Rp0*adXV)JMU9F* z{f};6k?R(mb#i9$#GF0M+LLxI`}?Ua@9&>`4+PF`ER0&OH_QB^qMz^-KY59+xmJ^^_PVgOB5E1n%?l`iI~YY*>}@2 zGrRNh-11Mgr?ALp$@9#erQ>(IBchKh>V)mf?^>C+)mn4H)b^@h+ubkz+f>^~BXlnR zgDpAk*OuI>n|saCSX+3RMXxyyHahuoE~-xw#lYT&J7#W&~4CCfA|?_GZ`OyB!rkL{UBe#)PLdMO!cRHx7aM+p_RXdZ@Ofq#s+bw6~C?dw_0yzbKN~<^V5W{>c^jM z?|OCX(KZpOSI3&4EIG{4A-(dK*L~KtaWbz%@~2O!+t<8TVCth#<4gTho=rG&qWXcQ zpTy^art<>l?^fTtwn1OyVYoqe&#m9zTKtN|Z+9CUKguN07*JiramN3cdfflMttIJK z-iMX0G*Yn7$~pA?D0j;BQ{U&^mj9P5<$K`e%xSClUdd^n7}Q#KF>2-xu4>8_FLhTDg4+k9|6 zFOXBZNur?Z(Jp=Ve`)taCdpXt*?7wLAIs%c(tUx;f>+O+bWz{z+?Ht%s*=us|J{_c zZmyjj+sQ){GV`Zi7Yn%gTJ1D@mAIquF739pi)NlzKd)xEPta1b^5XoL=`~pe&w}S? z9Ci^qYIeGr^;K&i(^R$_;xF&DEEoUz|Hk1A+32LK9s481_uPK@Q+1^e*AHJ^!S8%U ze8*Kg%a>USJ-4fQ`14MO#HT}>4}RG0UiaDc@3prXd{?$tePfoL@pgOmO06dM`A1o9 z8%;SB`1Zoh`-)2>1?9d-yIy;Fdqac0!7&-lsMT35+jbuog9&Ac)gSNY{$Zo znZM3Vv+a$saeXDwv~SbpHiL>8Wo)j ze{7j;F~gFNlZvOPew}T)=97kA%fx?$n)5Ys>t`B;O3VDUS6y`b!NadRg{CwyoSSHA zsFI$rwIfz{l4O^ipuPVB{mh1!xAH4l_trUba`QZrJLdiA*L9x@F%M1_vs4sp3$9IH zcVzmy#RAQ`jqNuNOCHicvt!NHdXW%_*BBU>{=e>7m{p{kAX>*1`lIE`x@VKGt2|g# zBd|_t?j1d8j>$K5HB?nan{6x4TeX)>>)0>%=;{M@mdCt@`o5IF3}N7u=4`uUoLsjc zNjm?S$n0MWrbKu|F{sz|?buet>e%T0R_OVJZsCdS|MoFoC@S3i%IMkuf65P5NVJND z*GMd#zngJk&?bxDvC>!1hZRM;++KEerojZ3sYUg&FEu~xh}+$okYmiq(HXZ$L;cQy zu4~UGKrNPzeVz1Yb!&R;(xV?GUzQtw)vkSIHMcf_Phh^@?fzf&=dRbZ1Rwd`-S2vM z`?Y%)m)Ep}CPzJbBPSWUXnJxY$Jcd=o4L(gSFdIK`zGT6@e`jm5x1Qt7 z*!oI!!jt6&3%|t|Du1lidH!b3vaIP7$_&ie*UeYiv~Tjc)x~=hI!$lHYx`}jlV3g| zl_|Mv{a+?Y8;j%7AJ=+KfAISxlTdsFo0R$<21`E%&G3b@8FbIi$lCdWt6U;r&DjRF z&OF^a%*^lgo{Gnp2lbV>o?2g0Gf954qhYwAO3hT(&YPOMUaUH>uCL{mbe5j3RnfM6 zOU!B(9hskbOyXMg-IR1) zqH|@vKQT-ZJT1>GbAs_|GhbX|oq@5Hq~O~jF`yWqvIveWL;7FnBXB3h2trhTeZ>1XK6 zhIuNf&Rrr4RxK564_f5E*k3Dm&%qd%FF#%VtJjwMt@p9c<*+?zt}eVaui}o+~Db`x7Tdt-lhMw(QhVQRemR!B4*U z`Suz`D+N4x-1cn2M2W)6R8Hxb-hB?mJA++K_de$1dRAT3vPAYO&w=e9|COikG_q)& zKKuLcwNo)~`XWtQ=f*t>;VrfQW%vF+?|oMD_MK5amId#cPD!&V`^MX--A%jrRCdd) z1rNpD1%o+0{V?0ZwR+22iB)@dS}{%g=Ce7{EoIdSUS8GpP1E8%p4s0&Xc=+%*+T>V zTkbQbhSV>6dB{@mLq$(JPwS_5(vf~W_J@M!&E9TYePO28&SPgfUNUJ+Yd`R{ROxK3 z$&b|MGY<#196$KsRen6f{kscoOb>N!FZ{PKzLnFlsA9s#KbhW>V;VMkIW)bHd9Sz4 zsl_!u%VBw-?y{99T(7108}t8MWGi>A=FFGVy-(sI>Ygi2UCzExXP&BRhC#{N$69~= zKYy2br#9(K-M!bJ7d$N7a{Zrm_~rnmg)D3InWZCtAZCNb zo;llMy4SnKSFx|#;T{xk{BOw$Z{|*gU$%XJ-(}e!L?nDME#foystzCHGh2uxVRXY{; za9mY<;}UjAXOGxFBiru<5$v`f-9K4!-eul$>h`On91mYHZ_+Bgwc;h?wb0toX%_oc zn@I1?N7I7k<%C?X*l##->T;4pql!&q zNct_!64$RC+c!pW-(DEobL_?G$5s6o55D`B+>^8M#s*Wv1#da~A4f5N+qviC%pj)E z8(GTE${g=r@;oB$3*Vs^V)>o@jK=lLqmJ+Dy%m4*3GaHg@(|bRu(`YRZcAUR_}|^( zQCY?`W$7zxWBv9eCrXvIc29e`;p92(4L+ym$}Y7@DcJI{^NLLoBfo61p5Oz$B>`P3 zCeIW1*~I=S{?b+Yi7`-i=6=3kdpW}nXKb}`51S#^kkq=OWl#KDry5~Dv*NCz*KFJe zu1t2jni6HMcJ7JV@i&(rZ%cFgFmG+xfy%oF=j@&}NBL9QkIiS*-5$QIGHL&o#{2Y* zdDr*A+dex~ST>h^d#-uZt0Ft)b$#k&heDp!7hAKVtpA@qbvWH!Pf&tk+Kp3}CC_{4 zez;&8ZOW2+VTToCKi9LBHuJkv{_LFjW%~oq1mz1$Kd8RBcC+Kiy#o_X{)in;KdgQ} ztL9$+VZlhJqnVtn5k_YYA7eXxFKlnqv>$4gjMWePXFhS8l>XH3+}#KFzc-02+^@N7 z=ZxvjN3K3<_54^VUA6MOkB*9;@ss_XFQtqRpE>l!c-`WT6~`Sjq~eU@E}ooWANy8q zSE+Y`Tji=UXa89$Q};;qHrNYnooTZpN_Mf)ubb?@zPsx#sN3_iYk~RYYp&V{vc4_z zn6*THZP%l)KmVEgu6Q)B%?{Y?*|vSFY2gu-Tw60|mJCDjZESK4+bx!-%`jQ}SnP90 z$J2GjJ5}WMIvPxbo~l)8!a48H&2KzCR`<%jE?c;A?TJla@@pF}PpLb4XtI@v z-o@I>3abUSxzvYkI=Gf2Zr6(C2cl02JDUbxT+POC@v6a>AKj`I1~$)bOZau4-D#(= zvCJg>?S03I0v6J?y(#vN%j`ZnZJlzsQNrZ!cflhYPq{tvbi8MJr1`H%tU6~p z^R`8KZ~ON87vH*=Bwby!oWH1NmS0I~xY+DO$y3%VBTjJf-QioWTZtk*E55*m(X1UxyuXQ8YV1-r>Z(i`NqkUbm7pCQXoypmk{nq@0 zje1JZsgF9d|J(1(d3Mub-Oe1xe$6xO)0H2d7dO6|wqWOmUve;t>7w=Z|m+FYK3m^6P)ZqG!cOA{tmR*LR)^6gO9eIgqiJ2xk2g^9`Iu+Q@f zSdX0PUVVFAYs9bMtJP|(D+HHpUAOql3qRSvBKMTpD^8xDRhPMCd)M3V&mMRm@9nle zs$L=Ekn!V>)UUm9y*t=eZr*nNt(n`Y^ZAR55AMBlJU`=4;fLr0Q#TbpPk1fsUFRDg z-1Q)Grv1Wqd$ctka!*`Up(wa$k$2Nv0ki#Y6{jAXxcGyV+p6D<%a*-Uxi{za{S@g< z0*4FcmF$W)zp=h%*^>oF)*aEmx?r{bU$I+!vYgu)UdgVTc3CsUueRO$K!(Bd%`+}? z-;*lbT~k-DH}_uk&ntmO$LfuOmoSLr$VL^EE`8$k8-@C`7JW3%z?{DB8&q>pdeVyN2!L6K_@?+bh!<)|w2?w1E&(h_a=FRkO zqP5J^ln9^Ry@wOl?M$Bj?sIk#D=+sG4#!VY>)N7DK$mm9yS6X0`h?1XQ1}E&4W zd5-ej=gU>Y&W0K7d6?D{<;c2Q{7&*0F|Df!R-!kenmF1HS2fi#w$BLn-EeKk9nNPw ze3zpao6L35zP#I_&}L$k>s__J+;1n7`}=ca*QrcA;?f{@{qgF1J&KC$?NjRGq`r5w zoqAQZMa2AHxP9Ri+4$p!w&(Bj^3o4nTy|sr<~wULPn=z|!g=PR(w2nHdwrVTuvR>O zFt3s8AYWwT?PANMMPJrzky`W7n@Kih?I-!*W%t7-?M`%cPy2WH9!tX_3!XJSK_BmS zF5pv{uQJDds^B)egMk(I7UsO2G*dU*`sCWY@HI!|#2J@M6}_`o*PL19Vyu4W)oC_G zX06R%);YZ1=xH+7y~5DYp`h>o)rbd9e|o<4Gn7u;>-$gsj7{3z8+oh#Hr(NQ!Iw9m zd(R9#H_bNFITAK6j?0DgPJFyNcFs!%tCIoo20~lc9tcZ+J$XiPQk2C?mrut6|2@~2 z7c+6)-)Aus zv+8t}Qxe593go5;S`)J$7}%~v+$Zj!GpC~7+~Bih7!m;CKco0BsqZ&ly2tmpMnyZ2YlOJT*QgE8xC4zf7r zw)XAaEE+EmTj`X0z-XcO{aK9jy&Fnx%Ql=`;#p{$HtWEB%?ZlOUo2d;o9kK2@d%!) zHFrP#WDRQ1o+(+-R<WQueR z?xspg9(--TX5IZTUjzLU|JZZR2x#fq9pcgQUwv!C{MWk$_lMnN)t;s_@rIPBpUQ>q z6+b0PjEcBJGk!l(Z%(t&Ox<(RRZrHwDlhsA<3%}UUdv7CZr|I4Jnr-S3(ih_Z`|~Q z#o2Ym7T)%iaqhRa`uFcxXOm4CBOPi*&IjhhWTi&w2oYAn^% z70Yf{D@!vJK302`KlprJi_gT2Vy0i4_&F0C>gW1$Fr^1a$2?qg*mGkM^TnRB`LAiz$d*xLU_UQ@KrY#gl!Mp$E3b%XL3+@@dV{r3PCN|yS*xN z#2FO3&w9*@i}=vA`KOp-n~lx=PW@;O z6MfX-_aUOaS1#63u4nFr*p(m54<{vu9x>k?Q9qBrtvM<&We)?za)>ALH zHbzg`&c%4uSZ{B!`GMH39GzJE^>va;mhUDoubG;Ds3t4bbk>T+@>9+z3N}nKZ#yDw z5tJxp@{m_ZEH|%3>%{?+*sZ2__TN<(Qr)x2`RwBPx!IF$i90SYJap_*dGg{m*-b*9 zzX;|i_a4muqH^=gQT0gnM9j z*oK$K?7Y_b#ns3f&6pgeAaccPu~Czz)H)kBM!CN1e~|~@^G@_$BJ`pw=Uv5{h_27? z7@b}=X$DKk%PgsM7Tk5EOZ3|HkIr5S6Z<=^&p7_AKuEsY&oiXSYlHCrZ7G?1cb^mT zyxf+`nEr0Q^3@{?AGUG2>QzOgC%s7zRQnqxX8qVAbgJpWR}wQac2BD;7br5am?Qkx zfJ2_fDqZhKAzPIbXWhIxKvcW%K*bvd8mE5_v;ny{hu z&t2R-UnG3XLQbf!+UaCfyuwa6uTX5sHI8>rj#&Qll z{ThA$+$x=J>jcTNKQ&K8<4f3@+diF-?R)vZaaL1im)--%x?Mli7k_rS`M{%g_ul>g z(lfdvtn{U4^;w_gR=u&)+v)i?jR#*=8?lvyU#)mcwx=`L)juC>E&xXO(}nA?Mfe`L55Wr>-~EotfAEoH?_3?sI8m+^tJB8H{Z9K zwn++3WAmFnO|AEM=p*UnAN|VX3=Zw_*30g!v-{xjjz1{)$=y|Nt!MvjGg(!5`?z^z z-R}Th`<0b!%~OODcLn+1T|4RLiSjehfK+R+9!C=&rEDeN`0O>{Y3Mrg-1GyY8(EgC9%#< z-lfs)#bw|xe*a_*hyO$NW*4FOv-cL*$Smym)N$?Pq-VS2?)g3WSy;I?&^^@q#`44S zx%N+ct624Pv4`NrtB1d(b?k|bOgZ*Q*=nY!{-aa;TbA52Te--n1K;h%H5(3x|C?e@4&k3MGA-B^%y zbLq2^lT*%#{5W{&q{iRNsZZ9<|C-Tp^6-&cWqCjE9?O=UH?@9!U|e99{})RUN#FnP z%w_tjjxK3zQ8z8yKgnNe8t2YH{g2s__y0v*pY}{;n&-6j??kq|X<_oQbJ!GqN9DfMinMzsDTB)A`De_3Yc)6r{di?cw|W2kf%?d)G}$ zJG=Q}zzzQBC9Q>}2~vN&BlhxKj@Z`u)pDQK(vraP2aBD9=UQH^_C0=sOQUa{O<{et zMPkXfvfjn+f}4#Uv`-u7)(Wi!V?`}8mSv(*;L z3LX2UUaxn2)Ai)&$3>3Mqw^oKe$4k@_4V#H-y?zbceN8)TmD&jb1avqEFJSl?qmW$gwd^{~jlt26~ZPm2@D>@u;4(M>We3U}Kn zdfVeD*Xgy2mB%jLlV9X3m^MM{_ob&l{z{hrJ$fThM^DhkIa2IT20zP*#p0!F8mH)7 zIh8WUXu+B6%vP@}DQiH)z;w zGqlK74R>Msr8Je*o9UHm(;}^%hxZkH^%fF7^!M26Z$_v28Ll7ZKc}p?TspfrIBMtC zgQqPFIj&{-n|n`KP-yugcv_#7(gLj#28&5u(((yyQWtgaUZ0Mn_MlsT0T2uz6A*(ANXBk?=`;#WGiZ&q=My7hcqY7ouv1Uy~6reu~gYxHgJ#`42Y)h@ph$^GPHQNCiy zA<+h%*H3)en&Vk#Tu6U^>crcYx(%)m;?!pT{J+k?L9gd!*$y+=*|rgJcVmjJbdGL* zVymMQ@%MLg=dqCd)~kn4&vSSne`(j5{BIX7|{yxdB z{=2y`TEWeYE`16@@*$bDJXu6%wuDErHotwAS==&)nVbc@glRG|ZMtY_Fd$esW z*QEYK3mt#1^ndQ}&iemO+H=>}zb4qtOxz*Et9Z19xz=LZ>*b$gdmo=&ky#fu&1&W9 zMJI0iu1YA|TqHRo$2s8nf~>ejyC=`;75rWJJ^%lymCK7+1m@)9#wmBWNGwGhiIu+LC5sGr2Z%^F*#BEylzQRMN`YXk||FoJ%6G%VUK6V#cL%i znw(!rY0h2!^!NX(TLL;#>-u8kuCt%G@mKC_-0ofcKiW6&H~H1~UCX_^=DYd&cZH!n zf1(ze&3b*CEt+-lmLFCwJ16A$R|ijDcU)%EpQ&kcg29dk%ZtmL$R$CO)rrqVTMKip@zb2;%~$9fkJ5A*1lKSoFO`d+U- z5%VtoE=#W2?&Y~FmiWzH(jj`x%(ve4{-WIrg>P!~|2;h2C~slBoZZQ@yT5IIQd*Sy zQ?mWx?Un!k$vf}+`DfOq6W1fpi`8@78!?N5jhjdSV5i?8{ z^PZq(`gjF%z`s5HYkb)4lvlWZn%*eq^!+P~Ugf;WuhfKgcFOFV+CS?F-_$Z+=RbG% zZ24y$)Aw^G^jNEdya{%+~q87G5pcTB83DtFo5d+ySod*|%2T@Z3@!2}(> z9EaTI)J=t6g=e3x`*`W)wtR~4T@+Cw;A!)Kp*9*;%{b_0sG^_cy&?;J)+bHs7`*aZ)%7tWd#v|{QT$wMJ!iHUbC%Wa!aa4x&y z5#ly`(u2=Wi%PO;~B={t_AQQ-~d@ zneYBRw}|eGu`hbi5N%~F`a~e;$9qd{jrjpVt5=z3ahixca=p{tlEF30SMFxGU)#S8 zdomAio_7D2>P3eOQ>NKIx;CAAY8~^OOam5~vh=_gR#(?;-d}y2t#66ir(cX4((n4s zQemI1`2N&Xt!e%fnKoR1q`b5L)&50{Q&t(~@s(Y3yTrdac8`O*uZ{g=tp!H#?)x`!U&lLthITIN$h2FX8lo3U`zNK(xFJ(_M{A$f8q2%ZzmD@6 zJ~P<-THiQjKZmsa->$D(`8)5_D>KV~jr?l1n?c3VLO)rx+jy_o+SHx?d!BC$+wtVT z@4tq_^BP6I?BLpFYVzdpwo@DQgw*mjF4nmpGG~gXXJK)KyGhpns+nh*j`1CuQhWB? z3g`Z7F^cPURNjBSHrw-0=kibK4!UfoKTUhbk$06P%q0I(%`(=NOrPHtY`MTT*Vym; z-qR)R&5{B?J3TLOZs$6=W%{4)|7uqc1YZ*3x)ZEFu~++;$>Z%WcBsYWO#d*Ev*&xj zeWrrj%Nh)GCiH)EYw5V@@M}>{)n)JXx2NbjPQ7zuP60=Wu-P^4Xx0Yhi0sq{C7%|a z3=49qn7iZiQI|s#?>tutsZ?FFH&}-w)X7pLt|;mJpCfHuNAfyi|2g{d``5dMgjz~$ z)L4FN-`8H}^mW-)vTX^QE+nem?%4d&`eZ0XPu}OEAzxj8c+wt*ZU-@sH z20Z5qT0dqT*Y}wAN$k9Y31b~|;HCa2$E@B=R-4Jy8MmwW>iJpxiOgQ5_jH`NCnO&X zsy?LrBkd;Bq-JH^-+pXSPqtVKuPNb{pSD;(KaQs{ri1UF#EqS5rzETNcNR4GKDy4e zBt!0_gGaaNN!R=a7w*P15-}_2%VVq&?w`<4#pMMmb@HA`nRf!{d z$2t!k)_2lgw#!nN+l9M;C3JNpdx31C-K?#pO(zYm9g1gM*LnAi@Q(*i+M1)YC$dcm z$Xqb%-lp?M0}@T#eH`PSnqE};@oiec&s({Up^9CltkYK3hHkToc3G*pMR{*`#{{vx z{p+0%ZD^8wEpR}#tAM|!{^#@`^4k~|Ra%iFc z$E_vS?{H-2pCyM({zTqWExvg&nP=wXX;pJRavVt9Bh~rhec%DV&_k?0f*b?WFV4BL z@#;#)6_o}*A{9ITUB3Hricd%WMirH%{}nz=>b9HlY|4YP`?NK@`tR;~D(xY;OM?IY zfqyxbRqAaYC$TuM_4)X&{9&R$ACH5v@59e3d;%VYu^V`9P4xPgW@d5Yk7?ebn{g9z zU$><9uu9qA__QzL`bm$)f-G_y+Gm(;wI0xHxN7z3((H&s2{sJ%`p@f8lZUK(NEj8L-G zGFDjl=Dvui#P+uQuRHuqja=FPZO;7cG4+J9A{$fIo^Y04!8KD~*V=ZaD7D7!Uw+`C zz?ACx84r`p8PX0G3fip*us5q!>)|R=pW1h3p8Q7r``@b0Ue^DlS zlhmOEvHfLIhHAx+BY!fwHS}_9Pib9pWs~foxsO`>@)`B>PPG_6;$5^x$MhoOYnPWA z6GR)gOC<)rj4$w3j8d3np?qCt+I)wV%ULom{B`6%+ptrN?aqD`!&R2ouhy&a)t_{V zDb8J7w{5q`6m!vuDxG@%(`xR;_Q!wtlpuQFD~V$;K8xe{c3aZD-+q-N50_ zv75iMyqyepDseci*tYm<$CbV_`IVRbmMmy~pQw}jP@eVbBSrP+6Gix?`DdS7Zf&Ye(m0t=31!Z_O3% zS)tmTkQrb!^;JpU*_F+{_s<6^#`YZQ)vvqmQeEO!JLi&;s8?;WZ*ShWM-HjiZ;HKL z%rGt3)T7asO)+}q-2DeEF39h6GA_F7Tl0OQ{;Y#PT4sHDQto2vXvJ6U&VT)ctYdl0 z7H#Qj*1Gu>DGQC(EiwqMyx#Y(tx4^2@>7N_4~~Xerg_$IPF`kSaO1|)>E%~FwqIDa zg!9mpSGwYdBZ5d5u+PLo6$}wn(q}bea3Z+rPKZEH3+e;+LV`x`!s4M7O^A zSLi2yX)^ao-NWZDcuU{B|Le7Mz>bHr78kg)Mg@d^zV^8H@Aa>X3;SI;e%LHB6`1EA zw4yX_Qcc`d+1*n9brM>UyWR%NSf)uQB}~;hbJ5IXrQlzo>Iqzh!uRwuD;6_UcQ2Dq z5WL4|rJQ5U7qN1pVeH0M?`q|)tRoirlRr*;-=Dlb^kK&NlLD`DxjHpPZ!nu3GWsL( zOSC+g(b8wz`z2;4cb}M+cAT%X=f3ng4HHE}8RHvziU}82m-F6GX!>hVu;6{{hc?BY z*vDD5J{H!?IUjA2TawqR>JT5=aB#1ty7q&E_y0{<_25P1S9{y+eNQUl{}=z-^efEh zyHRt@-o^lf$Ok$CPH%1&gefw5rAKV{_k8xXP1@J^yGHNx+v@k?I^J&RntOaT?;Cq* znQMM0zPxfcu+01At9^yBX}gX)B|kTvVgKAl?aa!}U+(NUzQ<8$)yu;-Y%D$<)!J5e z?(fIh1qbumQ#y>!I*T4tKhy7$ss445q3TAL%hkc{`?WicNow)-I^UOja#qn^c$@ad zrJ5QKlfL}Bcg6Z$$XS{1?^*sw3UL{2YTJ8qlRo=yL5a63T#Cfz-F36K5mmQZTCmV6 zG1i24&7Tc3VxE6V6R%aO(C0fE`0>%}aJxx!mfm>GP@1LUd9TA|L*aIJ`}tEZt_}V- z*F^5v zpP9UCe?sX0Yg}9Bo>7pr-t+m%47W49mVPsvt&Exh!%g^5q`m|@lx#RAlP0rgU+uiu}@XSTm`+K~9XZoMjdw1gK zyA|K}#*}|m-si}9_SEyWov~ikYeMEe**yE!*pt+g}m&w9q>eq;5b*L9nZOGTcl zlYN^mxMMc|JQghnSKdgJARL?Wix}z7onq5XG;9)`Ca=g>5D|+1^-i| zdm;1V#OaZbryskqVV!-~YDWS4F=+P|E4bT;XOl zf#q#k*^jz|b9nzNS4@0SVpqZF;BwYawJMbR)zeLuO_T4rWvxl_{ZapkV})73FCEv9 z5!+T4XemDIm|)H^BX7;ue=^%1g~xBHU0(RPs>d(IK>1x)dd=dr^IxUTxLYvhy zOSd0+Epqv$&)1rX7x(U*o z88!EwXy5&Lj-zIH8tj9 z6H1LK_uv2cX`-0W)%=$xo!yO$(JO8J8Dp)saP9vzW3AeUk1-D~nM~ul$Rnfg$1K|$ zxAl7d;&oGFE_2SakCF-4<8$)VS!Fq8H;x5s6hnj#9&ftlb#mrYZ_EA5a&;@8Kackh z-khZVMmk-_{DS>1)g3&1{E;iq_MS&}O zkjRc{a)}c&6?sBKf2p1-5MQ?~==twm6Qm4mw;bdXWn-`oS?_#s1D8q^=ax(FyVa+D z+!(QBTa&;Boy_=}m_TNGt^R{c=Uu-3O57!-=a^%$NbmvM1+MYTOiW^KWo7&BZA&~p z*WuVjQ62Fsa{U~}-HxiSCbMQn9Z-Ag`)2j;k&JW`le;+b~Yn|aB}Y`v?RwS0E_Cc zy+2p3C#y4Q?|U45l%woSru+ZMe)UoCUDet-Xp?SEGIzw3Gu9?z#o zOa5Oa)oj-DZC>x|So3t%*AWcWvPRoq?CL(gS6jC(A?(feEw3!sxv??vxE@oQw{`8o zQ|dcEim#osO@r^8%lW#`Jhfd5t;!l-B8R#loZ2D z-XCRcm^o2?^2vMl6^mD{bo1VHt@LKcv{r5NS*DqL_g+4&QfjOkwD8OFBR?J&szNx!vy*e_Z!6_smy$; zLMFQ^1Y4{-6(F?3Kxdi=8|U1FMgF}p)k&f2jW_Iam4De#w$VF#&beDIyphI9;u)=x z0>5?be4dGH`So{R?$Hz14L9$Xe4XXhwJl7(c&^vU1v)?OhQCmh3z@r7#{SYl=dO!a zX4p%~HETXUr;?l6K~y}5m1^PHIyK}EenZF%+Q?h4m8 z8)yC36x@CCfNY9o()HE6#(MX@TwAPv>5u=Bz>V+tL;Uh&!-Gp}*XTJEFS=s#M(z^( z#Am<7c``Z9{mlB&E&J{YW7{jcb-t+q4%gbXmz_~esqVJc)86Q5wM)Q;)$yUf#E3WS^1F_=)A@6+(WK=U!cs6P4y9A<*pjeTF70_qzqBtgZP=L~JtKa(JxgZK(O%EPmt4zl;CN ze#+bsog&Q7QJyf(gtLa9`<*+ZpHAA|UAcD-=zH!n@ticz_Lqpc@Yy+wFZ7 z%)Z!swv9%F-r+AV0~vdkI=^4mc>cjg{#AO{Ufan(zFiV|jcfU)7i#+&Cy7ZX?OU@_ z?15yYlE)_h;{u8;( z`DnwP$9uJAartg{FurhX*>zSE{qs`MD-F~xem-V@f6eWE`&T9Ead)eWT1Y;+#JT8@ z`n`jRpH#U_R)#;|I>FmfqnvS|R&43R*#}Ef{v9ml4(;c^B-pcZeOI-y%$q6umg;zB zZ%#cV_bx!!C0jq&EaA+Hz(?oJz8~40@-sHXQzP)|oULAm;}tIaO5bxPX38#;N$wB4 zcK)>!7k%{9`EibuxA$Ym4JST5jx*+F>-y+gz9GsZV*T%RQMm?ll?=Blep=`KOg-xB z8MjQK>c_g@rV28vPG51V`(l&$zwI}l#m=vcV@`^y-pn$I(>Z3V#%uR(b~UX|x58{5 z{i(+@3>qhHa0&DJu&hbA-Oy`ANHB-cDy}rqDCgn{yX!LZMK85JSDCduTC(oo^Q{^4 z)}6oFu|H#v75Bw|_Ms0POn;|sb=Ew5&c^@hnRMrKa?iMui}lPt_;6kJ9o$+my>IU}uHM#AyhC5Hig`=vmEVpkIvxfSb5uDB z_MTF{RsH*Ozv%QuS-+-yF1oZOi$j<5Z=uQR8Tmg_c%FMJM6Nu*d2jie{}OM@C*Bde zz>&ARiOod+!nH+OyuBIMH}PW3Tr%X+D*M|v$N4zHQY+R8Lz$5itoF`v4-P1at zW7YOcG7o;L+sxTnUguHQ`s&%e7xSjPX_>+pT6rQajoY~Ud z%iF!i?PLGa!UmI#iN;ManWDL6|I?BlJUS;_G<~C>+4>w?*-tH^i!3??b98pz&3$D! zb3)_Sb<;hb$b@cun7=vb&YZo^rk?uobltC*w8{y^g70f{WUMsU`j1$=F>bN^#; zJG1xd$)?UB8%nYk`6tyjm2x)f-feVvyXtpuMb3@Cta}%4)v`7DI%UU{O~;NrXpeVW zICsvRhO_fq7CM^NJd?ilt-Sx8k52&S-3@;p9g&+CE5n|({o@7Z@BY%)-@lg>z4qlu z;N_qF%g_2G#LmuX*btSp`RS~CvR_yJKHc~`?tTrwNc!p9N8a{-%($>8Xlm2J2+

PD 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 Ch&FEk literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-model.qdoc b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-model.qdoc index 9070ef0835b..4159269bc75 100644 --- a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-model.qdoc +++ b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-model.qdoc @@ -9,9 +9,9 @@ \title 3D Models The \l Components view features some built-in primitive 3D models. This allows - you to add cubes, cones, cylinders, and planes (rectangles) to your scene. + you to add cubes, cones, cylinders, spheres, and planes (rectangles) to your scene. - \image studio-3d-models.png "Various 3D models in the 3D view" + \image studio-3d-models.webp "Various 3D models in the 3D view" A model component loads mesh data from a file. You can modify how the component is shaded by using materials. For more information, see From ca9e72fe6cf9f8e0fe00d794bf7111abac372c54 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Mon, 5 Feb 2024 14:59:46 +0200 Subject: [PATCH 018/176] EffectComposer: Delete obsolete resource files at effect save If a new version of same effect is saved, remove files that are no longer part of the effect from the effect import dir after the save. Fixes: QDS-11737 Change-Id: Iae4da39f9f4713c2e26f1b90263881c8c9e13d78 Reviewed-by: Mahmoud Badri --- .../EffectCompositionNodeUniform.qml | 2 +- .../effectComposerQmlSources/ValueImage.qml | 27 ++++++++-- .../effectcomposer/effectcomposermodel.cpp | 49 ++++++++++++++++--- .../effectcomposernodesmodel.cpp | 10 +++- .../effectcomposer/effectcomposernodesmodel.h | 3 ++ .../effectcomposeruniformsmodel.cpp | 1 + .../effectcomposeruniformsmodel.h | 1 + .../effectcomposer/effectcomposerwidget.cpp | 5 ++ .../effectcomposer/effectcomposerwidget.h | 2 + src/plugins/effectcomposer/effectnode.cpp | 7 ++- src/plugins/effectcomposer/effectnode.h | 2 + src/plugins/effectcomposer/uniform.cpp | 8 +++ src/plugins/effectcomposer/uniform.h | 7 ++- 13 files changed, 109 insertions(+), 15 deletions(-) diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectCompositionNodeUniform.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectCompositionNodeUniform.qml index bfe0d3e3b78..c353f8260aa 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectCompositionNodeUniform.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectCompositionNodeUniform.qml @@ -50,7 +50,7 @@ Item { anchors.fill: parent Text { - text: uniformName + text: uniformDisplayName color: StudioTheme.Values.themeTextColor font.pixelSize: StudioTheme.Values.baseFontSize horizontalAlignment: Text.AlignRight diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueImage.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueImage.qml index b172eb302eb..3bdab3a0a0e 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueImage.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueImage.qml @@ -19,8 +19,10 @@ Row { onAbsoluteFilePathChanged: uniformValue = absoluteFilePath - function defaultAsString() { - let urlStr = uniformDefaultValue.toString() + function defaultAsString(defaultPath) { + if (!defaultPath) + return undefined + let urlStr = defaultPath.toString() urlStr = urlStr.replace(/^(file:\/{3})/, "") // Prepend slash if there is no drive letter @@ -30,7 +32,24 @@ Row { return urlStr } - defaultItems: uniformDefaultValue ? [uniformDefaultValue.split('/').pop()] : undefined - defaultPaths: uniformDefaultValue ? [defaultAsString(uniformDefaultValue)] : undefined + Component.onCompleted: { + let originalPath = defaultAsString( + EffectComposerBackend.rootView.uniformDefaultImage(nodeName, uniformName)) + let originalName = originalPath ? originalPath.split('/').pop() : undefined + if (originalName) { + defaultItems = [originalName] + defaultPaths = [originalPath] + } else { + let currentPath = uniformDefaultValue ? defaultAsString(uniformDefaultValue) : undefined + let currentName = currentPath ? currentPath.split('/').pop() : undefined + if (currentName) { + defaultItems = [currentName] + defaultPaths = [currentPath] + } else { + defaultItems = [] + defaultPaths = [] + } + } + } } } diff --git a/src/plugins/effectcomposer/effectcomposermodel.cpp b/src/plugins/effectcomposer/effectcomposermodel.cpp index 9a3b8ca0afc..9919fc7a308 100644 --- a/src/plugins/effectcomposer/effectcomposermodel.cpp +++ b/src/plugins/effectcomposer/effectcomposermodel.cpp @@ -678,6 +678,12 @@ R"( void EffectComposerModel::saveComposition(const QString &name) { + if (name.isEmpty() || name.size() < 3 || name[0].isLower()) { + QString error = QString("Error: Couldn't save composition '%1', name is invalid").arg(name); + qWarning() << error; + return; + } + const QString effectsAssetsDir = QmlDesigner::ModelNodeOperations::getEffectsDefaultDirectory(); const QString path = effectsAssetsDir + QDir::separator() + name + ".qep"; auto saveFile = QFile(path); @@ -708,9 +714,9 @@ void EffectComposerModel::saveComposition(const QString &name) saveFile.write(jsonDoc.toJson()); saveFile.close(); setCurrentComposition(name); - setHasUnsavedChanges(false); saveResources(name); + setHasUnsavedChanges(false); } void EffectComposerModel::openComposition(const QString &path) @@ -808,24 +814,31 @@ 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(); + Utils::FilePath effectPath = Utils::FilePath::fromString(effectsResPath); // Create the qmldir for effects - Utils::FilePath qmldirPath = effectsResDir.resolvePath(QStringLiteral("qmldir")); + QString qmldirFileName("qmldir"); + Utils::FilePath qmldirPath = effectsResDir.resolvePath(qmldirFileName); QString qmldirContent = QString::fromUtf8(qmldirPath.fileContents().value_or(QByteArray())); if (qmldirContent.isEmpty()) { qmldirContent.append("module Effects\n"); qmldirPath.writeFileContents(qmldirContent.toUtf8()); } + Utils::FilePaths oldFiles; + QStringList newFileNames; + // Create effect folder if not created - Utils::FilePath effectPath = Utils::FilePath::fromString(effectsResPath); if (!effectPath.exists()) { QDir effectDir(effectsResDir.toString()); effectDir.mkdir(name); + } else { + oldFiles = effectPath.dirEntries(QDir::Files); } // Create effect qmldir - qmldirPath = effectPath.resolvePath(QStringLiteral("qmldir")); + newFileNames.append(qmldirFileName); + qmldirPath = effectPath.resolvePath(qmldirFileName); qmldirContent = QString::fromUtf8(qmldirPath.fileContents().value_or(QByteArray())); if (qmldirContent.isEmpty()) { qmldirContent.append("module Effects."); @@ -857,13 +870,16 @@ void EffectComposerModel::saveResources(const QString &name) const QString qmlString = qmlStringList.join('\n'); QString qmlFilePath = effectsResPath + qmlFilename; writeToFile(qmlString.toUtf8(), qmlFilePath, FileType::Text); + newFileNames.append(qmlFilename); // Save shaders and images QStringList sources = {m_vertexShaderFilename, m_fragmentShaderFilename}; QStringList dests = {vsFilename, fsFilename}; + QHash fileNameToUniformHash; const QList uniforms = allUniforms(); - for (const Uniform *uniform : uniforms) { + bool hasSampler = false; + for (Uniform *uniform : uniforms) { if (uniform->type() == Uniform::Type::Sampler && !uniform->value().toString().isEmpty()) { QString imagePath = uniform->value().toString(); QFileInfo fi(imagePath); @@ -874,6 +890,8 @@ void EffectComposerModel::saveResources(const QString &name) } sources.append(imagePath); dests.append(imageFilename); + fileNameToUniformHash.insert(imageFilename, uniform); + hasSampler = true; } } @@ -893,11 +911,30 @@ void EffectComposerModel::saveResources(const QString &name) for (int i = 0; i < sources.count(); ++i) { Utils::FilePath source = Utils::FilePath::fromString(sources[i]); Utils::FilePath target = Utils::FilePath::fromString(effectsResPath + dests[i]); + newFileNames.append(target.fileName()); if (target.exists() && source.fileName() != target.fileName()) target.removeFile(); // Remove existing file for update - if (!source.copyFile(target)) qWarning() << __FUNCTION__ << " Failed to copy file: " << source; + + if (fileNameToUniformHash.contains(dests[i])) { + Uniform *uniform = fileNameToUniformHash[dests[i]]; + const QVariant newValue = target.toString(); + uniform->setDefaultValue(newValue); + uniform->setValue(newValue); + } + } + + // Delete old content that was not overwritten + for (const Utils::FilePath &oldFile : oldFiles) { + if (!newFileNames.contains(oldFile.fileName())) + oldFile.removeFile(); + } + + // Refresh UI to update sampler UrlChoosers + if (hasSampler) { + beginResetModel(); + endResetModel(); } emit resourcesSaved(QString("Effects.%1.%1").arg(name).toUtf8(), effectPath); diff --git a/src/plugins/effectcomposer/effectcomposernodesmodel.cpp b/src/plugins/effectcomposer/effectcomposernodesmodel.cpp index 28fc9434463..1d1d405e819 100644 --- a/src/plugins/effectcomposer/effectcomposernodesmodel.cpp +++ b/src/plugins/effectcomposer/effectcomposernodesmodel.cpp @@ -68,7 +68,10 @@ void EffectComposerNodesModel::loadModel() QDirIterator itEffects(categoryPath.toString(), {"*.qen"}, QDir::Files); while (itEffects.hasNext()) { itEffects.next(); - effects.push_back(new EffectNode(itEffects.filePath())); + auto node = new EffectNode(itEffects.filePath()); + if (!node->defaultImagesHash().isEmpty()) + m_defaultImagesHash.insert(node->name(), node->defaultImagesHash()); + effects.push_back(node); } catName[0] = catName[0].toUpper(); // capitalize first letter @@ -108,4 +111,9 @@ void EffectComposerNodesModel::updateCanBeAdded(const QStringList &uniforms) } } +QHash EffectComposerNodesModel::defaultImagesForNode(const QString &name) const +{ + return m_defaultImagesHash.value(name); +} + } // namespace EffectComposer diff --git a/src/plugins/effectcomposer/effectcomposernodesmodel.h b/src/plugins/effectcomposer/effectcomposernodesmodel.h index 7edabcd9f9f..ce914a5e6da 100644 --- a/src/plugins/effectcomposer/effectcomposernodesmodel.h +++ b/src/plugins/effectcomposer/effectcomposernodesmodel.h @@ -32,12 +32,15 @@ public: void updateCanBeAdded(const QStringList &uniforms); + QHash defaultImagesForNode(const QString &name) const; + private: QString nodesSourcesPath() const; QList m_categories; bool m_probeNodesDir = false; bool m_modelLoaded = false; + QHash> m_defaultImagesHash; }; } // namespace EffectComposer diff --git a/src/plugins/effectcomposer/effectcomposeruniformsmodel.cpp b/src/plugins/effectcomposer/effectcomposeruniformsmodel.cpp index 9d8c1a31a72..492d5a9e806 100644 --- a/src/plugins/effectcomposer/effectcomposeruniformsmodel.cpp +++ b/src/plugins/effectcomposer/effectcomposeruniformsmodel.cpp @@ -19,6 +19,7 @@ QHash EffectComposerUniformsModel::roleNames() const { QHash roles; roles[NameRole] = "uniformName"; + roles[DisplayNameRole] = "uniformDisplayName"; roles[DescriptionRole] = "uniformDescription"; roles[ValueRole] = "uniformValue"; roles[BackendValueRole] = "uniformBackendValue"; diff --git a/src/plugins/effectcomposer/effectcomposeruniformsmodel.h b/src/plugins/effectcomposer/effectcomposeruniformsmodel.h index f60c016ed09..65b2d7b2f00 100644 --- a/src/plugins/effectcomposer/effectcomposeruniformsmodel.h +++ b/src/plugins/effectcomposer/effectcomposeruniformsmodel.h @@ -30,6 +30,7 @@ public: private: enum Roles { NameRole = Qt::UserRole + 1, + DisplayNameRole, DescriptionRole, ValueRole, BackendValueRole, diff --git a/src/plugins/effectcomposer/effectcomposerwidget.cpp b/src/plugins/effectcomposer/effectcomposerwidget.cpp index 9303fe0ac41..b57120cb156 100644 --- a/src/plugins/effectcomposer/effectcomposerwidget.cpp +++ b/src/plugins/effectcomposer/effectcomposerwidget.cpp @@ -188,6 +188,11 @@ QPoint EffectComposerWidget::globalPos(const QPoint &point) const return point; } +QString EffectComposerWidget::uniformDefaultImage(const QString &nodeName, const QString &uniformName) const +{ + return m_effectComposerNodesModel->defaultImagesForNode(nodeName).value(uniformName); +} + QSize EffectComposerWidget::sizeHint() const { return {420, 420}; diff --git a/src/plugins/effectcomposer/effectcomposerwidget.h b/src/plugins/effectcomposer/effectcomposerwidget.h index c7349f30336..fb4c818f4f2 100644 --- a/src/plugins/effectcomposer/effectcomposerwidget.h +++ b/src/plugins/effectcomposer/effectcomposerwidget.h @@ -50,6 +50,8 @@ public: Q_INVOKABLE void doOpenComposition(); Q_INVOKABLE QRect screenRect() const; Q_INVOKABLE QPoint globalPos(const QPoint &point) const; + Q_INVOKABLE QString uniformDefaultImage(const QString &nodeName, + const QString &uniformName) const; QSize sizeHint() const override; diff --git a/src/plugins/effectcomposer/effectnode.cpp b/src/plugins/effectcomposer/effectnode.cpp index 73b00681542..6128e3a1969 100644 --- a/src/plugins/effectcomposer/effectnode.cpp +++ b/src/plugins/effectcomposer/effectnode.cpp @@ -31,8 +31,13 @@ EffectNode::EffectNode(const QString &qenPath) m_description = node.description(); const QList uniforms = node.uniforms(); - for (const Uniform *uniform : uniforms) + for (const Uniform *uniform : uniforms) { m_uniformNames.insert(uniform->name()); + if (uniform->type() == Uniform::Type::Sampler) { + m_defaultImagesHash.insert( + uniform->name(), uniform->defaultValue().toString()); + } + } } QString EffectNode::name() const diff --git a/src/plugins/effectcomposer/effectnode.h b/src/plugins/effectcomposer/effectnode.h index 6322ff29f85..dff553a2de5 100644 --- a/src/plugins/effectcomposer/effectnode.h +++ b/src/plugins/effectcomposer/effectnode.h @@ -25,6 +25,7 @@ public: QString name() const; QString description() const; QString qenPath() const; + QHash defaultImagesHash() const { return m_defaultImagesHash; } void setCanBeAdded(bool enabled); @@ -40,6 +41,7 @@ private: QUrl m_iconPath; bool m_canBeAdded = true; QSet m_uniformNames; + QHash m_defaultImagesHash; }; } // namespace EffectComposer diff --git a/src/plugins/effectcomposer/uniform.cpp b/src/plugins/effectcomposer/uniform.cpp index 4c8b994388e..348c7b3a2b0 100644 --- a/src/plugins/effectcomposer/uniform.cpp +++ b/src/plugins/effectcomposer/uniform.cpp @@ -102,6 +102,14 @@ void Uniform::setValue(const QVariant &newValue) } } +void Uniform::setDefaultValue(const QVariant &newValue) +{ + if (m_defaultValue != newValue) { + m_defaultValue = newValue; + emit uniformDefaultValueChanged(); + } +} + QVariant Uniform::defaultValue() const { return m_defaultValue; diff --git a/src/plugins/effectcomposer/uniform.h b/src/plugins/effectcomposer/uniform.h index 09081248c54..92e02bc467e 100644 --- a/src/plugins/effectcomposer/uniform.h +++ b/src/plugins/effectcomposer/uniform.h @@ -18,7 +18,8 @@ class Uniform : public QObject { Q_OBJECT - Q_PROPERTY(QString uniformName MEMBER m_displayName CONSTANT) + Q_PROPERTY(QString uniformName MEMBER m_name CONSTANT) + Q_PROPERTY(QString uniformDisplayName MEMBER m_displayName CONSTANT) Q_PROPERTY(QString uniformType READ typeName CONSTANT) Q_PROPERTY(QString uniformControlType READ controlTypeName CONSTANT) Q_PROPERTY(QString uniformDescription READ description CONSTANT) @@ -26,7 +27,7 @@ class Uniform : public QObject Q_PROPERTY(QVariant uniformBackendValue READ backendValue NOTIFY uniformBackendValueChanged) Q_PROPERTY(QVariant uniformMinValue MEMBER m_minValue CONSTANT) Q_PROPERTY(QVariant uniformMaxValue MEMBER m_maxValue CONSTANT) - Q_PROPERTY(QVariant uniformDefaultValue MEMBER m_defaultValue CONSTANT) + Q_PROPERTY(QVariant uniformDefaultValue MEMBER m_defaultValue NOTIFY uniformDefaultValueChanged) Q_PROPERTY(QVariant uniformUseCustomValue MEMBER m_useCustomValue CONSTANT) public: @@ -52,6 +53,7 @@ public: QVariant value() const; void setValue(const QVariant &newValue); + void setDefaultValue(const QVariant &newValue); QVariant backendValue() const; @@ -80,6 +82,7 @@ public: signals: void uniformValueChanged(); void uniformBackendValueChanged(); + void uniformDefaultValueChanged(); private: QString mipmapPropertyName(const QString &name) const; From c58efc4310d25bdbd62766f0efa087cbdafcd1f1 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Fri, 2 Feb 2024 14:33:52 +0200 Subject: [PATCH 019/176] QmlDesigner: Implement new data store structure for Model Editor Task-number: QDS-11778 Change-Id: Ia98fee976e5d81acc608b6209da270cbee2f9c61 Reviewed-by: Mahmoud Badri Reviewed-by: Miikka Heikkinen Reviewed-by: Qt CI Patch Build Bot --- .../ImportDialog.qml | 2 +- .../shared-plugin/name/models.json.tpl | 82 ++- .../collectioneditor/collectiondetails.cpp | 613 ++++++++++++------ .../collectioneditor/collectiondetails.h | 60 +- .../collectiondetailsmodel.cpp | 320 +++------ .../collectioneditor/collectiondetailsmodel.h | 9 +- .../collectioneditorutils.cpp | 257 +++----- .../collectioneditor/collectioneditorutils.h | 11 +- .../collectionsourcemodel.cpp | 4 +- .../collectioneditor/collectionsourcemodel.h | 2 +- .../collectioneditor/collectionview.cpp | 13 +- .../collectioneditor/collectionwidget.cpp | 82 ++- .../collectioneditor/collectionwidget.h | 10 +- .../collectioneditor/datastoremodelnode.cpp | 15 +- .../collectioneditor/datastoremodelnode.h | 8 +- 15 files changed, 791 insertions(+), 697 deletions(-) diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/ImportDialog.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/ImportDialog.qml index 3f8108005cb..cb77d567e8c 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/ImportDialog.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/ImportDialog.qml @@ -192,7 +192,7 @@ StudioControls.Dialog { enabled: root.fileExists && collectionName.text !== "" onClicked: { - let collectionImported = root.backendValue.importCollectionToDataStore( + let collectionImported = root.backendValue.importFile( collectionName.text, fileName.text) diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/shared-plugin/name/models.json.tpl b/share/qtcreator/qmldesigner/studio_templates/projects/shared-plugin/name/models.json.tpl index 8ebda6fb7e6..ca9c1736517 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/shared-plugin/name/models.json.tpl +++ b/share/qtcreator/qmldesigner/studio_templates/projects/shared-plugin/name/models.json.tpl @@ -1,30 +1,56 @@ { - "book": [ - { - "author": "Nigel Rees", - "category": "reference", - "price": 8.95, - "title": "Sayings of the Century" - }, - { - "author": "Evelyn Waugh", - "category": "fiction", - "price": 12.99, - "title": "Sword of Honor" - }, - { - "author": "Herman Melville", - "category": "fiction", - "isbn": "0-553-21311-3", - "price": 8.99, - "title": "Moby Dick" - }, - { - "author": "J. R. R. Tolkien", - "category": "fiction", - "isbn": "0-395-19395-8", - "price": 22.99, - "title": "The Lord of the Rings" - } - ] + "book": { + "columns": [ + { + "name": "author", + "type": "String" + }, + { + "name": "category", + "type": "String" + }, + { + "name": "isbn", + "type": "String" + }, + { + "name": "price", + "type": "Real" + }, + { + "name": "title", + "type": "String" + } + ], + "data": [ + [ + "Nigel Rees", + "reference", + "", + 8.95, + "Sayings of the Century" + ], + [ + "Evelyn Waugh", + "fiction", + "", + 12.99, + "Sword of Honor" + ], + [ + "Herman Melville", + "fiction", + "0-553-21311-3", + 8.99, + "Moby Dick" + ], + [ + "J. R. R. Tolkien", + "fiction", + "0-395-19395-8", + 22.99, + "The Lord of the Rings" + ] + ] + } } diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp index f35e9f2ed26..f1827ee6be6 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp @@ -3,16 +3,25 @@ #include "collectiondetails.h" +#include "collectioneditorutils.h" + #include #include #include #include #include +#include #include #include namespace QmlDesigner { +#define COLLERR_OK QT_TRANSLATE_NOOP("CollectioParseError", "no error occurred") +#define COLLERR_MAINOBJECT QT_TRANSLATE_NOOP("CollectioParseError", "Document object not found") +#define COLLERR_COLLECTIONNAME QT_TRANSLATE_NOOP("CollectioParseError", "Model name not found") +#define COLLERR_COLLECTIONOBJ QT_TRANSLATE_NOOP("CollectioParseError", "Model is not an object") +#define COLLERR_COLUMNARRAY QT_TRANSLATE_NOOP("CollectioParseError", "Column is not an array") +#define COLLERR_UNKNOWN QT_TRANSLATE_NOOP("CollectioParseError", "Unknown error") struct CollectionProperty { @@ -28,30 +37,23 @@ const QMap DataTypeWarning::dataTypeWarnings class CollectionDetails::Private { - using SourceFormat = CollectionEditorConstants::SourceFormat; - public: QList properties; - QList elements; - SourceFormat sourceFormat = SourceFormat::Unknown; + QList dataRecords; CollectionReference reference; bool isChanged = false; bool isValidColumnId(int column) const { return column > -1 && column < properties.size(); } - bool isValidRowId(int row) const { return row > -1 && row < elements.size(); } + bool isValidRowId(int row) const { return row > -1 && row < dataRecords.size(); } }; inline static bool isValidColorName(const QString &colorName) { -#if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0) - return QColor::isValidColorName(colorName); -#else constexpr QStringView colorPattern( u"(?^(?:#(?:(?:[0-9a-fA-F]{2}){3,4}|(?:[0-9a-fA-F]){3,4}))$)"); static const QRegularExpression colorRegex(colorPattern.toString()); return colorRegex.match(colorName).hasMatch(); -#endif // >= Qt 6.4 } /** @@ -115,7 +117,7 @@ inline static bool getCustomUrl(const QString &value, return false; } -static CollectionProperty::DataType collectionDataTypeFromJsonValue(const QJsonValue &value) +static CollectionProperty::DataType dataTypeFromJsonValue(const QJsonValue &value) { using DataType = CollectionDetails::DataType; using JsonType = QJsonValue::Type; @@ -133,6 +135,10 @@ static CollectionProperty::DataType collectionDataTypeFromJsonValue(const QJsonV } case JsonType::String: { const QString stringValue = value.toString(); + + if (stringValue.isEmpty()) + return DataType::Unknown; + if (isValidColorName(stringValue)) return DataType::Color; @@ -147,6 +153,42 @@ static CollectionProperty::DataType collectionDataTypeFromJsonValue(const QJsonV } } +static QList getColumnsFromImportedJsonArray(const QJsonArray &importedArray) +{ + using DataType = CollectionDetails::DataType; + + QHash resultSet; + QList result; + + for (const QJsonValue &value : importedArray) { + if (value.isObject()) { + const QJsonObject object = value.toObject(); + QJsonObject::ConstIterator element = object.constBegin(); + const QJsonObject::ConstIterator stopItem = object.constEnd(); + + while (element != stopItem) { + const QString propertyName = element.key(); + if (resultSet.contains(propertyName)) { + CollectionProperty &property = result[resultSet.value(propertyName)]; + if (property.type == DataType::Unknown) { + property.type = dataTypeFromJsonValue(element.value()); + } else if (property.type == DataType::Integer) { + const DataType currentCellDataType = dataTypeFromJsonValue(element.value()); + if (currentCellDataType == DataType::Real) + property.type = currentCellDataType; + } + } else { + result.append({propertyName, dataTypeFromJsonValue(element.value())}); + resultSet.insert(propertyName, resultSet.size()); + } + ++element; + } + } + } + + return result; +} + static QVariant valueToVariant(const QJsonValue &value, CollectionDetails::DataType type) { using DataType = CollectionDetails::DataType; @@ -214,6 +256,30 @@ static QJsonValue variantToJsonValue( } } +inline static bool isEmptyJsonValue(const QJsonValue &value) +{ + return value.isNull() || value.isUndefined() || (value.isString() && value.toString().isEmpty()); +} + +QString CollectionParseError::errorString() const +{ + switch (errorNo) { + case NoError: + return COLLERR_OK; + case MainObjectMissing: + return COLLERR_MAINOBJECT; + case CollectionNameNotFound: + return COLLERR_COLLECTIONNAME; + case CollectionIsNotObject: + return COLLERR_COLLECTIONOBJ; + case ColumnsBlockIsNotArray: + return COLLERR_COLUMNARRAY; + case UnknownError: + default: + return COLLERR_UNKNOWN; + } +} + CollectionDetails::CollectionDetails() : d(new Private()) {} @@ -224,66 +290,70 @@ CollectionDetails::CollectionDetails(const CollectionReference &reference) d->reference = reference; } +void CollectionDetails::resetData(const QJsonDocument &localDocument, + const QString &collectionToImport, + CollectionParseError *error) +{ + CollectionDetails importedCollection = fromLocalJson(localDocument, collectionToImport, error); + d->properties.swap(importedCollection.d->properties); + d->dataRecords.swap(importedCollection.d->dataRecords); +} + CollectionDetails::CollectionDetails(const CollectionDetails &other) = default; CollectionDetails::~CollectionDetails() = default; -void CollectionDetails::resetDetails(const QStringList &propertyNames, - const QList &elements, - CollectionEditorConstants::SourceFormat format) -{ - if (!isValid()) - return; - - d->properties = Utils::transform(propertyNames, [](const QString &name) -> CollectionProperty { - return {name, DataType::Unknown}; - }); - - d->elements = elements; - d->sourceFormat = format; - - resetPropertyTypes(); - markSaved(); -} - void CollectionDetails::insertColumn(const QString &propertyName, int colIdx, const QVariant &defaultValue, DataType type) { - if (!isValid()) - return; - if (containsPropertyName(propertyName)) return; CollectionProperty property = {propertyName, type}; - if (d->isValidColumnId(colIdx)) + if (d->isValidColumnId(colIdx)) { d->properties.insert(colIdx, property); - else + } else { + colIdx = d->properties.size(); d->properties.append(property); + } - QJsonValue defaultJsonValue = QJsonValue::fromVariant(defaultValue); - for (QJsonObject &element : d->elements) - element.insert(propertyName, defaultJsonValue); + const QJsonValue defaultJsonValue = QJsonValue::fromVariant(defaultValue); + for (QJsonArray &record : d->dataRecords) + record.insert(colIdx, defaultJsonValue); markChanged(); } bool CollectionDetails::removeColumns(int colIdx, int count) { - if (count < 1 || !isValid() || !d->isValidColumnId(colIdx)) + if (!d->isValidColumnId(colIdx)) return false; int maxCount = d->properties.count() - colIdx; count = std::min(maxCount, count); - const QList removedProperties = d->properties.mid(colIdx, count); + if (count < 1) + return false; + d->properties.remove(colIdx, count); - for (const CollectionProperty &property : removedProperties) { - for (QJsonObject &element : d->elements) - element.remove(property.name); + for (QJsonArray &record : d->dataRecords) { + QJsonArray newElement; + + auto elementItr = record.constBegin(); + auto elementEnd = elementItr + colIdx; + while (elementItr != elementEnd) + newElement.append(*(elementItr++)); + + elementItr += count; + elementEnd = record.constEnd(); + + while (elementItr != elementEnd) + newElement.append(*(elementItr++)); + + record = newElement; } markChanged(); @@ -291,67 +361,31 @@ bool CollectionDetails::removeColumns(int colIdx, int count) return true; } -void CollectionDetails::insertElementAt(std::optional object, int row) +void CollectionDetails::insertEmptyRows(int row, int count) { - if (!isValid()) - return; - - auto insertJson = [this, row](const QJsonObject &jsonObject) { - if (d->isValidRowId(row)) - d->elements.insert(row, jsonObject); - else - d->elements.append(jsonObject); - }; - - if (object.has_value()) { - insertJson(object.value()); - } else { - QJsonObject defaultObject; - for (const CollectionProperty &property : std::as_const(d->properties)) - defaultObject.insert(property.name, {}); - insertJson(defaultObject); - } - - markChanged(); -} - -void CollectionDetails::insertEmptyElements(int row, int count) -{ - if (!isValid()) - return; - if (count < 1) return; row = qBound(0, row, rows()); - d->elements.insert(row, count, {}); + + insertRecords({}, row, count); markChanged(); } -bool CollectionDetails::removeElements(int row, int count) +bool CollectionDetails::removeRows(int row, int count) { - if (count < 1 || !isValid() || !d->isValidRowId(row)) + if (!d->isValidRowId(row)) return false; - int maxCount = d->elements.count() - row; + int maxCount = d->dataRecords.count() - row; count = std::min(maxCount, count); - QSet removedProperties; - Utils::span elementsSpan{std::as_const(d->elements)}; - for (const QJsonObject &element : elementsSpan.subspan(row, count)) { - const QStringList elementPropertyNames = element.keys(); - for (const QString &removedProperty : elementPropertyNames) - removedProperties.insert(removedProperty); - } - - d->elements.remove(row, count); - - for (const QString &removedProperty : removedProperties) - resetPropertyType(removedProperty); + if (count < 1) + return false; + d->dataRecords.remove(row, count); markChanged(); - return true; } @@ -360,13 +394,12 @@ bool CollectionDetails::setPropertyValue(int row, int column, const QVariant &va if (!d->isValidRowId(row) || !d->isValidColumnId(column)) return false; - QJsonObject &element = d->elements[row]; QVariant currentValue = data(row, column); - if (value == currentValue) return false; - element.insert(d->properties.at(column).name, variantToJsonValue(value, typeAt(column))); + QJsonArray &record = d->dataRecords[row]; + record.replace(column, variantToJsonValue(value, typeAt(column))); markChanged(); return true; } @@ -377,17 +410,10 @@ bool CollectionDetails::setPropertyName(int column, const QString &value) return false; const CollectionProperty &oldProperty = d->properties.at(column); - const QString oldColumnName = oldProperty.name; - if (oldColumnName == value) + if (oldProperty.name == value) return false; d->properties.replace(column, {value, oldProperty.type}); - for (QJsonObject &element : d->elements) { - if (element.contains(oldColumnName)) { - element.insert(value, element.value(oldColumnName)); - element.remove(oldColumnName); - } - } markChanged(); return true; @@ -395,7 +421,7 @@ bool CollectionDetails::setPropertyName(int column, const QString &value) bool CollectionDetails::setPropertyType(int column, DataType type) { - if (!isValid() || !d->isValidColumnId(column)) + if (!d->isValidColumnId(column)) return false; bool changed = false; @@ -406,12 +432,12 @@ bool CollectionDetails::setPropertyType(int column, DataType type) const DataType formerType = property.type; property.type = type; - for (QJsonObject &element : d->elements) { - if (element.contains(property.name)) { - const QJsonValue value = element.value(property.name); + for (QJsonArray &rowData : d->dataRecords) { + if (column < rowData.size()) { + const QJsonValue value = rowData.at(column); const QVariant properTypedValue = valueToVariant(value, formerType); const QJsonValue properTypedJsonValue = variantToJsonValue(properTypedValue, type); - element.insert(property.name, properTypedJsonValue); + rowData.replace(column, properTypedJsonValue); changed = true; } } @@ -427,29 +453,15 @@ CollectionReference CollectionDetails::reference() const return d->reference; } -CollectionEditorConstants::SourceFormat CollectionDetails::sourceFormat() const -{ - return d->sourceFormat; -} - QVariant CollectionDetails::data(int row, int column) const { - if (!isValid()) - return {}; - if (!d->isValidRowId(row)) return {}; if (!d->isValidColumnId(column)) return {}; - const QString &propertyName = d->properties.at(column).name; - const QJsonObject &elementNode = d->elements.at(row); - - if (!elementNode.contains(propertyName)) - return {}; - - const QJsonValue cellValue = elementNode.value(propertyName); + const QJsonValue cellValue = d->dataRecords.at(row).at(column); if (typeAt(column) == DataType::Image) { const QUrl imageUrl = valueToVariant(cellValue, DataType::Image).toUrl(); @@ -482,48 +494,37 @@ CollectionDetails::DataType CollectionDetails::typeAt(int row, int column) const if (!d->isValidRowId(row) || !d->isValidColumnId(column)) return {}; - const QString &propertyName = d->properties.at(column).name; - const QJsonObject &element = d->elements.at(row); - - if (element.contains(propertyName)) - return collectionDataTypeFromJsonValue(element.value(propertyName)); - - return {}; + const QJsonValue cellData = d->dataRecords.at(row).at(column); + return dataTypeFromJsonValue(cellData); } DataTypeWarning::Warning CollectionDetails::cellWarningCheck(int row, int column) const { - const QString &propertyName = d->properties.at(column).name; - const QJsonObject &element = d->elements.at(row); + const QJsonValue cellValue = d->dataRecords.at(row).at(column); const DataType columnType = typeAt(column); const DataType cellType = typeAt(row, column); - if (columnType == DataType::Unknown || element.isEmpty() - || data(row, column) == QVariant::fromValue(nullptr)) { - return DataTypeWarning::Warning::None; - } - if (element.contains(propertyName)) { - if (columnType == DataType::Real && cellType == DataType::Integer) - return DataTypeWarning::Warning::None; - else if (columnType != cellType) - return DataTypeWarning::Warning::CellDataTypeMismatch; - } + if (columnType == DataType::Unknown || isEmptyJsonValue(cellValue)) + return DataTypeWarning::Warning::None; + + if (columnType == DataType::Real && cellType == DataType::Integer) + return DataTypeWarning::Warning::None; + + if (columnType != cellType) + return DataTypeWarning::Warning::CellDataTypeMismatch; return DataTypeWarning::Warning::None; } -bool CollectionDetails::containsPropertyName(const QString &propertyName) +bool CollectionDetails::containsPropertyName(const QString &propertyName) const { - if (!isValid()) - return false; - return Utils::anyOf(d->properties, [&propertyName](const CollectionProperty &property) { return property.name == propertyName; }); } -bool CollectionDetails::isValid() const +bool CollectionDetails::hasValidReference() const { return d->reference.node.isValid() && d->reference.name.size(); } @@ -540,7 +541,7 @@ int CollectionDetails::columns() const int CollectionDetails::rows() const { - return d->elements.size(); + return d->dataRecords.size(); } bool CollectionDetails::markSaved() @@ -565,6 +566,90 @@ void CollectionDetails::resetReference(const CollectionReference &reference) } } +QString CollectionDetails::toJson() const +{ + QJsonArray exportedArray; + const int propertyCount = d->properties.count(); + + for (const QJsonArray &record : std::as_const(d->dataRecords)) { + const int valueCount = std::min(int(record.count()), propertyCount); + + QJsonObject exportedElement; + for (int i = 0; i < valueCount; ++i) { + const QJsonValue &value = record.at(i); + if (!isEmptyJsonValue(value)) + exportedElement.insert(d->properties.at(i).name, value); + } + + exportedArray.append(exportedElement); + } + + return QString::fromUtf8(QJsonDocument(exportedArray).toJson()); +} + +QString CollectionDetails::toCsv() const +{ + QString content; + + auto gotoNextLine = [&content]() { + if (content.size() && content.back() == ',') + content.back() = '\n'; + else + content += "\n"; + }; + + const int propertyCount = d->properties.count(); + if (propertyCount <= 0) + return ""; + + for (const CollectionProperty &property : std::as_const(d->properties)) + content += property.name + ','; + + gotoNextLine(); + + for (const QJsonArray &record : std::as_const(d->dataRecords)) { + const int valueCount = std::min(int(record.count()), propertyCount); + int i = 0; + for (; i < valueCount; ++i) { + const QJsonValue &value = record.at(i); + + if (value.isDouble()) + content += QString::number(value.toDouble()) + ','; + else + content += value.toString() + ','; + } + + for (; i < propertyCount; ++i) + content += ','; + + gotoNextLine(); + } + + return content; +} + +QJsonObject CollectionDetails::toLocalJson() const +{ + QJsonObject collectionObject; + QJsonArray columnsArray; + QJsonArray dataArray; + + for (const CollectionProperty &property : std::as_const(d->properties)) { + QJsonObject columnObject; + columnObject.insert("name", property.name); + columnObject.insert("type", CollectionEditorUtils::dataTypeToString(property.type)); + columnsArray.append(columnObject); + } + + for (const QJsonArray &record : std::as_const(d->dataRecords)) + dataArray.append(record); + + collectionObject.insert("columns", columnsArray); + collectionObject.insert("data", dataArray); + + return collectionObject; +} + void CollectionDetails::registerDeclarativeType() { typedef CollectionDetails::DataType DataType; @@ -575,6 +660,116 @@ void CollectionDetails::registerDeclarativeType() qmlRegisterUncreatableType("CollectionDetails", 1, 0, "Warning", "Enum type"); } +CollectionDetails CollectionDetails::fromImportedCsv(const QByteArray &document) +{ + QStringList headers; + QJsonArray importedArray; + + QTextStream stream(document); + + if (!stream.atEnd()) + headers = stream.readLine().split(','); + + for (QString &header : headers) + header = header.trimmed(); + + if (!headers.isEmpty()) { + while (!stream.atEnd()) { + const QStringList recordDataList = stream.readLine().split(','); + int column = -1; + QJsonObject recordData; + for (const QString &cellData : recordDataList) { + if (++column == headers.size()) + break; + recordData.insert(headers.at(column), cellData); + } + importedArray.append(recordData); + } + } + + return fromImportedJson(importedArray); +} + +CollectionDetails CollectionDetails::fromImportedJson(const QJsonDocument &document) +{ + QJsonArray importedCollection; + auto refineJsonArray = [](const QJsonArray &array) -> QJsonArray { + QJsonArray resultArray; + for (const QJsonValue &collectionData : array) { + if (collectionData.isObject()) { + QJsonObject rowObject = collectionData.toObject(); + const QStringList rowKeys = rowObject.keys(); + for (const QString &key : rowKeys) { + const QJsonValue cellValue = rowObject.value(key); + if (cellValue.isArray()) + rowObject.remove(key); + } + resultArray.push_back(rowObject); + } + } + return resultArray; + }; + + if (document.isArray()) { + importedCollection = refineJsonArray(document.array()); + } else if (document.isObject()) { + QJsonObject documentObject = document.object(); + const QStringList mainKeys = documentObject.keys(); + + bool arrayFound = false; + for (const QString &key : mainKeys) { + const QJsonValue value = documentObject.value(key); + if (value.isArray()) { + arrayFound = true; + importedCollection = refineJsonArray(value.toArray()); + break; + } + } + + if (!arrayFound) { + QJsonObject singleObject; + for (const QString &key : mainKeys) { + const QJsonValue value = documentObject.value(key); + + if (!value.isObject()) + singleObject.insert(key, value); + } + importedCollection.push_back(singleObject); + } + } + + return fromImportedJson(importedCollection); +} + +CollectionDetails CollectionDetails::fromLocalJson(const QJsonDocument &document, + const QString &collectionName, + CollectionParseError *error) +{ + auto setError = [&error](CollectionParseError::ParseError parseError) { + if (error) + error->errorNo = parseError; + }; + + setError(CollectionParseError::NoError); + + if (document.isObject()) { + QJsonObject collectionMap = document.object(); + if (collectionMap.contains(collectionName)) { + QJsonValue collectionValue = collectionMap.value(collectionName); + if (collectionValue.isObject()) + return fromLocalCollection(collectionValue.toObject()); + else + setError(CollectionParseError::CollectionIsNotObject); + } else { + setError(CollectionParseError::CollectionNameNotFound); + } + } else { + setError(CollectionParseError::MainObjectMissing); + } + + return CollectionDetails{}; +} + CollectionDetails &CollectionDetails::operator=(const CollectionDetails &other) { CollectionDetails value(other); @@ -587,81 +782,87 @@ void CollectionDetails::markChanged() d->isChanged = true; } -void CollectionDetails::resetPropertyType(const QString &propertyName) +void CollectionDetails::insertRecords(const QJsonArray &record, int idx, int count) { - for (CollectionProperty &property : d->properties) { - if (property.name == propertyName) - resetPropertyType(property); + if (count < 1) + return; + + QJsonArray localRecord; + const int columnsCount = columns(); + for (int i = 0; i < columnsCount; i++) { + const QJsonValue originalCellData = record.at(i); + if (originalCellData.isArray()) + localRecord.append({}); + else + localRecord.append(originalCellData); } + + if (idx > d->dataRecords.size() || idx < 0) + idx = d->dataRecords.size(); + + d->dataRecords.insert(idx, count, localRecord); } -void CollectionDetails::resetPropertyType(CollectionProperty &property) +CollectionDetails CollectionDetails::fromImportedJson(const QJsonArray &importedArray) { - const QString &propertyName = property.name; - DataType columnType = DataType::Unknown; - for (const QJsonObject &element : std::as_const(d->elements)) { - if (element.contains(propertyName)) { - const DataType cellType = collectionDataTypeFromJsonValue(element.value(propertyName)); - if (cellType != DataType::Unknown) { - if (columnType == DataType::Integer && cellType != DataType::Real) - continue; + const QList columnData = getColumnsFromImportedJsonArray(importedArray); + QList localJsonArray; + for (const QJsonValue &importedRowValue : importedArray) { + QJsonObject importedRowObject = importedRowValue.toObject(); + QJsonArray localRow; + for (const CollectionProperty &property : columnData) + localRow.append(importedRowObject.value(property.name)); + localJsonArray.append(localRow); + } + CollectionDetails result; + result.d->properties = columnData; + result.d->dataRecords = localJsonArray; + result.markSaved(); - columnType = cellType; - if (columnType == DataType::Integer) - continue; + return result; +} - break; +CollectionDetails CollectionDetails::fromLocalCollection(const QJsonObject &localCollection, + CollectionParseError *error) +{ + auto setError = [&error](CollectionParseError::ParseError parseError) { + if (error) + error->errorNo = parseError; + }; + + CollectionDetails result; + setError(CollectionParseError::NoError); + + if (localCollection.contains("columns")) { + const QJsonValue columnsValue = localCollection.value("columns"); + if (columnsValue.isArray()) { + const QJsonArray columns = columnsValue.toArray(); + for (const QJsonValue &columnValue : columns) { + if (columnValue.isObject()) { + const QJsonObject column = columnValue.toObject(); + const QString columnName = column.value("name").toString(); + if (!columnName.isEmpty()) { + result.insertColumn(columnName, + -1, + {}, + CollectionEditorUtils::dataTypeFromString( + column.value("type").toString())); + } + } } + + if (int columnsCount = result.columns()) { + const QJsonArray dataRecords = localCollection.value("data").toArray(); + for (const QJsonValue &dataRecordValue : dataRecords) + result.insertRecords(dataRecordValue.toArray()); + } + } else { + setError(CollectionParseError::ColumnsBlockIsNotArray); + return result; } } - property.type = columnType; -} -void CollectionDetails::resetPropertyTypes() -{ - for (CollectionProperty &property : d->properties) - resetPropertyType(property); -} - -QJsonArray CollectionDetails::getCollectionAsJsonArray() const -{ - QJsonArray collectionArray; - - for (const QJsonObject &element : std::as_const(d->elements)) - collectionArray.push_back(element); - - return collectionArray; -} - -QString CollectionDetails::getCollectionAsJsonString() const -{ - return QString::fromUtf8(QJsonDocument(getCollectionAsJsonArray()).toJson()); -} - -QString CollectionDetails::getCollectionAsCsvString() const -{ - QString content; - if (d->properties.count() <= 0) - return ""; - - for (const CollectionProperty &property : std::as_const(d->properties)) - content += property.name + ','; - - content.back() = '\n'; - - for (const QJsonObject &elementsRow : std::as_const(d->elements)) { - for (const CollectionProperty &property : std::as_const(d->properties)) { - const QJsonValue &value = elementsRow.value(property.name); - - if (value.isDouble()) - content += QString::number(value.toDouble()) + ','; - else - content += value.toString() + ','; - } - content.back() = '\n'; - } - - return content; + return result; } } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h index a18c557c52b..2ccc69e4477 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h @@ -3,7 +3,6 @@ #pragma once -#include "collectioneditorconstants.h" #include "modelnode.h" #include @@ -36,8 +35,6 @@ struct CollectionReference struct CollectionProperty; struct DataTypeWarning { - Q_GADGET - public: enum Warning { None, CellDataTypeMismatch }; Q_ENUM(Warning) @@ -47,14 +44,31 @@ public: : warning(warning) {} - static QString getDataTypeWarningString(Warning warning) { + static QString getDataTypeWarningString(Warning warning) + { return dataTypeWarnings.value(warning); } private: + Q_GADGET static const QMap dataTypeWarnings; }; +struct CollectionParseError +{ + enum ParseError { + NoError, + MainObjectMissing, + CollectionNameNotFound, + CollectionIsNotObject, + ColumnsBlockIsNotArray, + UnknownError + }; + + ParseError errorNo = ParseError::NoError; + QString errorString() const; +}; + class CollectionDetails { Q_GADGET @@ -68,33 +82,32 @@ public: CollectionDetails(const CollectionDetails &other); ~CollectionDetails(); - void resetDetails(const QStringList &propertyNames, - const QList &elements, - CollectionEditorConstants::SourceFormat format); + void resetData(const QJsonDocument &localDocument, + const QString &collectionToImport, + CollectionParseError *error = nullptr); + void insertColumn(const QString &propertyName, int colIdx = -1, const QVariant &defaultValue = {}, DataType type = DataType::Unknown); bool removeColumns(int colIdx, int count = 1); - void insertElementAt(std::optional object, int row = -1); - void insertEmptyElements(int row = 0, int count = 1); - bool removeElements(int row, int count = 1); + void insertEmptyRows(int row = 0, int count = 1); + bool removeRows(int row, int count = 1); bool setPropertyValue(int row, int column, const QVariant &value); bool setPropertyName(int column, const QString &value); bool setPropertyType(int column, DataType type); CollectionReference reference() const; - CollectionEditorConstants::SourceFormat sourceFormat() const; QVariant data(int row, int column) const; QString propertyAt(int column) const; DataType typeAt(int column) const; DataType typeAt(int row, int column) const; DataTypeWarning::Warning cellWarningCheck(int row, int column) const; - bool containsPropertyName(const QString &propertyName); + bool containsPropertyName(const QString &propertyName) const; - bool isValid() const; + bool hasValidReference() const; bool isChanged() const; int columns() const; @@ -104,23 +117,32 @@ public: void swap(CollectionDetails &other); void resetReference(const CollectionReference &reference); - QString getCollectionAsJsonString() const; - QString getCollectionAsCsvString() const; - QJsonArray getCollectionAsJsonArray() const; + QString toJson() const; + QString toCsv() const; + QJsonObject toLocalJson() const; static void registerDeclarativeType(); + static CollectionDetails fromImportedCsv(const QByteArray &document); + static CollectionDetails fromImportedJson(const QJsonDocument &document); + static CollectionDetails fromLocalJson(const QJsonDocument &document, + const QString &collectionName, + CollectionParseError *error = nullptr); + CollectionDetails &operator=(const CollectionDetails &other); private: void markChanged(); - void resetPropertyType(const QString &propertyName); - void resetPropertyType(CollectionProperty &property); - void resetPropertyTypes(); + void insertRecords(const QJsonArray &record, int idx = -1, int count = 1); + + static CollectionDetails fromImportedJson(const QJsonArray &importedArray); + static CollectionDetails fromLocalCollection(const QJsonObject &localCollection, + CollectionParseError *error = nullptr); // The private data is supposed to be shared between the copies class Private; QSharedPointer d; }; + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp index 51e3be9ad6b..17a26c58c5c 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp @@ -3,7 +3,6 @@ #include "collectiondetailsmodel.h" -#include "collectioneditorconstants.h" #include "collectioneditorutils.h" #include "modelnode.h" @@ -13,116 +12,11 @@ #include #include -#include -#include #include #include #include #include -namespace { - -QStringList getJsonHeaders(const QJsonArray &collectionArray) -{ - QSet resultSet; - QList result; - - for (const QJsonValue &value : collectionArray) { - if (value.isObject()) { - const QJsonObject object = value.toObject(); - QJsonObject::ConstIterator element = object.constBegin(); - const QJsonObject::ConstIterator stopItem = object.constEnd(); - - while (element != stopItem) { - const QString property = element.key(); - if (!resultSet.contains(property)) { - result.append(property); - resultSet.insert(property); - } - ++element; - } - } - } - - return result; -} - -class CollectionDataTypeHelper -{ -public: - using DataType = QmlDesigner::CollectionDetails::DataType; - - static QString typeToString(DataType dataType) - { - static const QHash typeStringHash = typeToStringHash(); - return typeStringHash.value(dataType); - } - - static DataType typeFromString(const QString &dataType) - { - static const QHash stringTypeHash = stringToTypeHash(); - return stringTypeHash.value(dataType, DataType::Unknown); - } - - static QStringList typesStringList() - { - static const QStringList typesList = orderedTypeNames(); - return typesList; - } - -private: - CollectionDataTypeHelper() = delete; - - static QHash typeToStringHash() - { - return { - {DataType::Unknown, "Unknown"}, - {DataType::String, "String"}, - {DataType::Url, "Url"}, - {DataType::Real, "Real"}, - {DataType::Integer, "Integer"}, - {DataType::Boolean, "Boolean"}, - {DataType::Image, "Image"}, - {DataType::Color, "Color"}, - }; - } - - static QHash stringToTypeHash() - { - QHash stringTypeHash; - const QHash typeStringHash = typeToStringHash(); - for (const auto &transferItem : typeStringHash.asKeyValueRange()) - stringTypeHash.insert(transferItem.second, transferItem.first); - - return stringTypeHash; - } - - static QStringList orderedTypeNames() - { - const QList orderedtypes{ - DataType::String, - DataType::Integer, - DataType::Real, - DataType::Image, - DataType::Color, - DataType::Url, - DataType::Boolean, - DataType::Unknown, - }; - - QStringList orderedNames; - QHash typeStringHash = typeToStringHash(); - - for (const DataType &type : orderedtypes) - orderedNames.append(typeStringHash.take(type)); - - Q_ASSERT(typeStringHash.isEmpty()); - return orderedNames; - } -}; - -} // namespace - namespace QmlDesigner { CollectionDetailsModel::CollectionDetailsModel(QObject *parent) @@ -161,6 +55,8 @@ QVariant CollectionDetailsModel::data(const QModelIndex &index, int role) const if (!index.isValid()) return {}; + QTC_ASSERT(m_currentCollection.hasValidReference(), return {}); + if (role == SelectedRole) return (index.column() == m_selectedColumn || index.row() == m_selectedRow); @@ -181,6 +77,8 @@ QVariant CollectionDetailsModel::data(const QModelIndex &index, int role) const bool CollectionDetailsModel::setData(const QModelIndex &index, const QVariant &value, int role) { + QTC_ASSERT(m_currentCollection.hasValidReference(), return false); + if (!index.isValid()) return {}; @@ -208,6 +106,8 @@ bool CollectionDetailsModel::setHeaderData(int section, const QVariant &value, [[maybe_unused]] int role) { + QTC_ASSERT(m_currentCollection.hasValidReference(), return false); + if (orientation == Qt::Vertical) return false; @@ -220,13 +120,15 @@ bool CollectionDetailsModel::setHeaderData(int section, bool CollectionDetailsModel::insertRows(int row, int count, [[maybe_unused]] const QModelIndex &parent) { + QTC_ASSERT(m_currentCollection.hasValidReference(), return false); + if (count < 1) return false; row = qBound(0, row, rowCount()); beginResetModel(); - m_currentCollection.insertEmptyElements(row, count); + m_currentCollection.insertEmptyRows(row, count); endResetModel(); selectRow(row); @@ -235,6 +137,8 @@ bool CollectionDetailsModel::insertRows(int row, int count, [[maybe_unused]] con bool CollectionDetailsModel::removeColumns(int column, int count, const QModelIndex &parent) { + QTC_ASSERT(m_currentCollection.hasValidReference(), return false); + if (column < 0 || column >= columnCount(parent) || count < 1) return false; @@ -258,12 +162,14 @@ bool CollectionDetailsModel::removeColumns(int column, int count, const QModelIn bool CollectionDetailsModel::removeRows(int row, int count, const QModelIndex &parent) { + QTC_ASSERT(m_currentCollection.hasValidReference(), return false); + if (row < 0 || row >= rowCount(parent) || count < 1) return false; count = std::min(count, rowCount(parent) - row); beginRemoveRows(parent, row, row + count - 1); - bool rowsRemoved = m_currentCollection.removeElements(row, count); + bool rowsRemoved = m_currentCollection.removeRows(row, count); endRemoveRows(); ensureSingleCell(); @@ -282,7 +188,7 @@ QVariant CollectionDetailsModel::headerData(int section, Qt::Orientation orienta { if (orientation == Qt::Horizontal) { if (role == DataTypeRole) - return CollectionDataTypeHelper::typeToString(m_currentCollection.typeAt(section)); + return CollectionEditorUtils::dataTypeToString(m_currentCollection.typeAt(section)); else return m_currentCollection.propertyAt(section); } @@ -295,6 +201,8 @@ QVariant CollectionDetailsModel::headerData(int section, Qt::Orientation orienta CollectionDetails::DataType CollectionDetailsModel::propertyDataType(int column) const { + QTC_ASSERT(m_currentCollection.hasValidReference(), return CollectionDetails::DataType::Unknown); + return m_currentCollection.typeAt(column); } @@ -310,21 +218,29 @@ int CollectionDetailsModel::selectedRow() const QString CollectionDetailsModel::propertyName(int column) const { + QTC_ASSERT(m_currentCollection.hasValidReference(), return {}); + return m_currentCollection.propertyAt(column); } QString CollectionDetailsModel::propertyType(int column) const { - return CollectionDataTypeHelper::typeToString(m_currentCollection.typeAt(column)); + QTC_ASSERT(m_currentCollection.hasValidReference(), return {}); + + return CollectionEditorUtils::dataTypeToString(m_currentCollection.typeAt(column)); } bool CollectionDetailsModel::isPropertyAvailable(const QString &name) { + QTC_ASSERT(m_currentCollection.hasValidReference(), return false); + return m_currentCollection.containsPropertyName(name); } bool CollectionDetailsModel::addColumn(int column, const QString &name, const QString &propertyType) { + QTC_ASSERT(m_currentCollection.hasValidReference(), return false); + if (m_currentCollection.containsPropertyName(name)) return false; @@ -335,7 +251,7 @@ bool CollectionDetailsModel::addColumn(int column, const QString &name, const QS m_currentCollection.insertColumn(name, column, {}, - CollectionDataTypeHelper::typeFromString(propertyType)); + CollectionEditorUtils::dataTypeFromString(propertyType)); endInsertColumns(); return m_currentCollection.containsPropertyName(name); } @@ -379,8 +295,10 @@ bool CollectionDetailsModel::renameColumn(int section, const QString &newValue) bool CollectionDetailsModel::setPropertyType(int column, const QString &newValue) { + QTC_ASSERT(m_currentCollection.hasValidReference(), return false); + bool changed = m_currentCollection.setPropertyType(column, - CollectionDataTypeHelper::typeFromString( + CollectionEditorUtils::dataTypeFromString( newValue)); if (changed) { emit headerDataChanged(Qt::Horizontal, column, column); @@ -430,7 +348,7 @@ void CollectionDetailsModel::deselectAll() QStringList CollectionDetailsModel::typesList() { - return CollectionDataTypeHelper::typesStringList(); + return CollectionEditorUtils::dataTypesStringList(); } void CollectionDetailsModel::loadCollection(const ModelNode &sourceNode, const QString &collection) @@ -451,10 +369,7 @@ void CollectionDetailsModel::loadCollection(const ModelNode &sourceNode, const Q } else { deselectAll(); switchToCollection(newReference); - if (sourceNode.type() == CollectionEditorConstants::JSONCOLLECTIONMODEL_TYPENAME) - loadJsonCollection(fileName, collection); - else if (sourceNode.type() == CollectionEditorConstants::CSVCOLLECTIONMODEL_TYPENAME) - loadCsvCollection(fileName, collection); + loadJsonCollection(fileName, collection); } } @@ -516,7 +431,7 @@ bool CollectionDetailsModel::saveDataStoreCollections() for (CollectionDetails &openedCollection : m_openedCollections) { const CollectionReference reference = openedCollection.reference(); if (reference.node == node) { - obj.insert(reference.name, openedCollection.getCollectionAsJsonArray()); + obj.insert(reference.name, openedCollection.toLocalJson()); collectionsToBeSaved << openedCollection; } } @@ -545,14 +460,14 @@ bool CollectionDetailsModel::exportCollection(const QUrl &url) using Utils::FilePath; using Utils::TextFileFormat; - QTC_ASSERT(m_currentCollection.isValid(), return false); + QTC_ASSERT(m_currentCollection.hasValidReference(), return false); bool saved = false; const FilePath filePath = FilePath::fromUserInput(url.isLocalFile() ? url.toLocalFile() : url.toString()); const QString saveFormat = filePath.toFileInfo().suffix().toLower(); - const QString content = saveFormat == "csv" ? m_currentCollection.getCollectionAsCsvString() - : m_currentCollection.getCollectionAsJsonString(); + const QString content = saveFormat == "csv" ? m_currentCollection.toCsv() + : m_currentCollection.toJson(); TextFileFormat textFileFormat; textFileFormat.codec = EditorManager::defaultTextCodec(); @@ -566,6 +481,49 @@ bool CollectionDetailsModel::exportCollection(const QUrl &url) return saved; } +const CollectionDetails CollectionDetailsModel::upToDateConstCollection( + const CollectionReference &reference) const +{ + using Utils::FilePath; + using Utils::FileReader; + CollectionDetails collection; + + if (m_openedCollections.contains(reference)) { + collection = m_openedCollections.value(reference); + } else { + QUrl url = CollectionEditorUtils::getSourceCollectionPath(reference.node); + FilePath path = FilePath::fromUserInput(url.isLocalFile() ? url.toLocalFile() + : url.toString()); + FileReader file; + + if (!file.fetch(path)) + return collection; + + QJsonParseError jpe; + QJsonDocument document = QJsonDocument::fromJson(file.data(), &jpe); + + if (jpe.error != QJsonParseError::NoError) + return collection; + + collection = CollectionDetails::fromLocalJson(document, reference.name); + collection.resetReference(reference); + } + return collection; +} + +bool CollectionDetailsModel::collectionHasColumn(const CollectionReference &reference, + const QString &columnName) const +{ + const CollectionDetails collection = upToDateConstCollection(reference); + return collection.containsPropertyName(columnName); +} + +QString CollectionDetailsModel::getFirstColumnName(const CollectionReference &reference) const +{ + const CollectionDetails collection = upToDateConstCollection(reference); + return collection.propertyAt(0); +} + void CollectionDetailsModel::updateEmpty() { bool isEmptyNow = rowCount() == 0; @@ -603,113 +561,25 @@ void CollectionDetailsModel::closeCollectionIfSaved(const CollectionReference &c void CollectionDetailsModel::closeCurrentCollectionIfSaved() { - if (m_currentCollection.isValid()) { + if (m_currentCollection.hasValidReference()) { closeCollectionIfSaved(m_currentCollection.reference()); m_currentCollection = CollectionDetails{}; } } -void CollectionDetailsModel::loadJsonCollection(const QString &source, const QString &collection) +void CollectionDetailsModel::loadJsonCollection(const QString &filePath, const QString &collection) { - using CollectionEditorConstants::SourceFormat; + QJsonDocument document = readJsonFile(filePath); - QFile sourceFile(source); - QJsonArray collectionNodes; - bool jsonFileIsOk = false; - if (sourceFile.open(QFile::ReadOnly)) { - QJsonParseError jpe; - QJsonDocument document = QJsonDocument::fromJson(sourceFile.readAll(), &jpe); - if (jpe.error == QJsonParseError::NoError) { - jsonFileIsOk = true; - if (document.isObject()) { - QJsonObject collectionMap = document.object(); - if (collectionMap.contains(collection)) { - QJsonValue collectionVal = collectionMap.value(collection); - if (collectionVal.isArray()) - collectionNodes = collectionVal.toArray(); - else - collectionNodes.append(collectionVal); - } - } - } - } - - if (collectionNodes.isEmpty()) { - endResetModel(); - return; - }; - - QList elements; - for (const QJsonValue &value : std::as_const(collectionNodes)) { - if (value.isObject()) { - QJsonObject object = value.toObject(); - elements.append(object); - } - } - - SourceFormat sourceFormat = jsonFileIsOk ? SourceFormat::Json : SourceFormat::Unknown; beginResetModel(); - m_currentCollection.resetDetails(getJsonHeaders(collectionNodes), elements, sourceFormat); - ensureSingleCell(); - endResetModel(); -} - -void CollectionDetailsModel::loadCsvCollection(const QString &source, - [[maybe_unused]] const QString &collectionName) -{ - using CollectionEditorConstants::SourceFormat; - - QFile sourceFile(source); - QStringList headers; - QList elements; - bool csvFileIsOk = false; - - if (sourceFile.open(QFile::ReadOnly)) { - QTextStream stream(&sourceFile); - - if (!stream.atEnd()) - headers = stream.readLine().split(','); - - if (!headers.isEmpty()) { - while (!stream.atEnd()) { - const QStringList recordDataList = stream.readLine().split(','); - int column = -1; - QJsonObject recordData; - for (const QString &cellData : recordDataList) { - if (++column == headers.size()) - break; - recordData.insert(headers.at(column), cellData); - } - if (recordData.count()) - elements.append(recordData); - } - csvFileIsOk = true; - } - } - - for (const QString &header : std::as_const(headers)) { - for (QJsonObject &element: elements) { - QVariant variantValue; - if (element.contains(header)) { - variantValue = variantFromString(element.value(header).toString()); - element[header] = variantValue.toJsonValue(); - - if (variantValue.isValid()) - break; - } - } - } - - SourceFormat sourceFormat = csvFileIsOk ? SourceFormat::Csv : SourceFormat::Unknown; - beginResetModel(); - m_currentCollection.resetDetails(headers, elements, sourceFormat); + m_currentCollection.resetData(document, collection); ensureSingleCell(); endResetModel(); } void CollectionDetailsModel::ensureSingleCell() { - if (!m_currentCollection.isValid()) + if (!m_currentCollection.hasValidReference()) return; if (!columnCount()) @@ -748,6 +618,27 @@ QVariant CollectionDetailsModel::variantFromString(const QString &value) return QVariant::fromValue(value); } +QJsonDocument CollectionDetailsModel::readJsonFile(const QUrl &url) +{ + using Utils::FilePath; + using Utils::FileReader; + FilePath path = FilePath::fromUserInput(url.isLocalFile() ? url.toLocalFile() : url.toString()); + FileReader file; + + if (!file.fetch(path)) { + emit warning(tr("File reading problem"), file.errorString()); + return {}; + } + + QJsonParseError jpe; + QJsonDocument document = QJsonDocument::fromJson(file.data(), &jpe); + + if (jpe.error != QJsonParseError::NoError) + emit warning(tr("Json parse error"), jpe.errorString()); + + return document; +} + void CollectionDetailsModel::setCollectionName(const QString &newCollectionName) { if (m_collectionName != newCollectionName) { @@ -761,5 +652,4 @@ QString CollectionDetailsModel::warningToString(DataTypeWarning::Warning warning return DataTypeWarning::getDataTypeWarningString(warning); } - } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h index cef942b0448..1fe152eaa24 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h @@ -68,11 +68,16 @@ public: Q_INVOKABLE bool saveDataStoreCollections(); Q_INVOKABLE bool exportCollection(const QUrl &url); + const CollectionDetails upToDateConstCollection(const CollectionReference &reference) const; + bool collectionHasColumn(const CollectionReference &reference, const QString &columnName) const; + QString getFirstColumnName(const CollectionReference &reference) const; + signals: void collectionNameChanged(const QString &collectionName); void selectedColumnChanged(int); void selectedRowChanged(int); void isEmptyChanged(bool); + void warning(const QString &title, const QString &body); private slots: void updateEmpty(); @@ -82,10 +87,10 @@ private: void closeCollectionIfSaved(const CollectionReference &collection); void closeCurrentCollectionIfSaved(); void setCollectionName(const QString &newCollectionName); - void loadJsonCollection(const QString &source, const QString &collection); - void loadCsvCollection(const QString &source, const QString &collectionName); + void loadJsonCollection(const QString &filePath, const QString &collection); void ensureSingleCell(); QVariant variantFromString(const QString &value); + QJsonDocument readJsonFile(const QUrl &url); QHash m_openedCollections; CollectionDetails m_currentCollection; diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp index 4b779c52fa5..9c777249323 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp @@ -24,19 +24,77 @@ #include #include +using DataType = QmlDesigner::CollectionDetails::DataType; + namespace { using CollectionDataVariant = std::variant; +class CollectionDataTypeHelper +{ +private: + using DataType = QmlDesigner::CollectionDetails::DataType; + friend QString QmlDesigner::CollectionEditorUtils::dataTypeToString(DataType); + friend DataType QmlDesigner::CollectionEditorUtils::dataTypeFromString(const QString &); + friend QStringList QmlDesigner::CollectionEditorUtils::dataTypesStringList(); + + CollectionDataTypeHelper() = delete; + + static QHash typeToStringHash() + { + return { + {DataType::Unknown, "Unknown"}, + {DataType::String, "String"}, + {DataType::Url, "Url"}, + {DataType::Real, "Real"}, + {DataType::Integer, "Integer"}, + {DataType::Boolean, "Boolean"}, + {DataType::Image, "Image"}, + {DataType::Color, "Color"}, + }; + } + + static QHash stringToTypeHash() + { + QHash stringTypeHash; + const QHash typeStringHash = typeToStringHash(); + for (const auto &transferItem : typeStringHash.asKeyValueRange()) + stringTypeHash.insert(transferItem.second, transferItem.first); + + return stringTypeHash; + } + + static QStringList orderedTypeNames() + { + const QList orderedtypes{ + DataType::String, + DataType::Integer, + DataType::Real, + DataType::Image, + DataType::Color, + DataType::Url, + DataType::Boolean, + DataType::Unknown, + }; + + QStringList orderedNames; + QHash typeStringHash = typeToStringHash(); + + for (const DataType &type : orderedtypes) + orderedNames.append(typeStringHash.take(type)); + + Q_ASSERT(typeStringHash.isEmpty()); + return orderedNames; + } +}; + inline bool operator<(const QColor &a, const QColor &b) { return a.name(QColor::HexArgb) < b.name(QColor::HexArgb); } -inline CollectionDataVariant valueToVariant(const QVariant &value, - QmlDesigner::CollectionDetails::DataType type) +inline CollectionDataVariant valueToVariant(const QVariant &value, DataType type) { - using DataType = QmlDesigner::CollectionDetails::DataType; switch (type) { case DataType::String: return value.toString(); @@ -112,7 +170,7 @@ inline Utils::FilePath qmlDirFilePath() namespace QmlDesigner::CollectionEditorUtils { -bool variantIslessThan(const QVariant &a, const QVariant &b, CollectionDetails::DataType type) +bool variantIslessThan(const QVariant &a, const QVariant &b, DataType type) { return std::visit(LessThanVisitor{}, valueToVariant(a, type), valueToVariant(b, type)); } @@ -210,16 +268,6 @@ bool isDataStoreNode(const ModelNode &dataStoreNode) return modelPath.isSameFile(expectedFile); } -QJsonArray defaultCollectionArray() -{ - QJsonObject initialObject; - QJsonArray initialCollection; - - initialObject.insert("Column1", ""); - initialCollection.append(initialObject); - return initialCollection; -} - bool ensureDataStoreExists(bool &justCreated) { using Utils::FilePath; @@ -306,178 +354,43 @@ bool ensureDataStoreExists(bool &justCreated) return false; } -QJsonArray loadAsSingleJsonCollection(const QUrl &url) +QJsonObject defaultCollection() { - QFile file(url.isLocalFile() ? url.toLocalFile() : url.toString()); - QJsonArray collection; - QByteArray jsonData; - if (file.open(QFile::ReadOnly)) - jsonData = file.readAll(); + QJsonObject collectionObject; - file.close(); - if (jsonData.isEmpty()) - return {}; + QJsonArray columns; + QJsonObject defaultColumn; + defaultColumn.insert("name", "Column 1"); + defaultColumn.insert("type", "string"); + columns.append(defaultColumn); - QJsonParseError parseError; - QJsonDocument document = QJsonDocument::fromJson(jsonData, &parseError); - if (parseError.error != QJsonParseError::NoError) - return {}; + QJsonArray collectionData; + QJsonArray cellData; + cellData.append(QString{}); + collectionData.append(cellData); - auto refineJsonArray = [](const QJsonArray &array) -> QJsonArray { - QJsonArray resultArray; - for (const QJsonValue &collectionData : array) { - if (collectionData.isObject()) { - QJsonObject rowObject = collectionData.toObject(); - const QStringList rowKeys = rowObject.keys(); - for (const QString &key : rowKeys) { - QJsonValue cellValue = rowObject.value(key); - if (cellValue.isArray()) - rowObject.remove(key); - } - resultArray.push_back(rowObject); - } - } - return resultArray; - }; + collectionObject.insert("columns", columns); + collectionObject.insert("data", collectionData); - if (document.isArray()) { - collection = refineJsonArray(document.array()); - } else if (document.isObject()) { - QJsonObject documentObject = document.object(); - const QStringList mainKeys = documentObject.keys(); - - bool arrayFound = false; - for (const QString &key : mainKeys) { - const QJsonValue &value = documentObject.value(key); - if (value.isArray()) { - arrayFound = true; - collection = refineJsonArray(value.toArray()); - break; - } - } - - if (!arrayFound) { - QJsonObject singleObject; - for (const QString &key : mainKeys) { - const QJsonValue value = documentObject.value(key); - - if (!value.isObject()) - singleObject.insert(key, value); - } - collection.push_back(singleObject); - } - } - return collection; + return collectionObject; } -QJsonArray loadAsCsvCollection(const QUrl &url) +QString dataTypeToString(CollectionDetails::DataType dataType) { - QFile sourceFile(url.isLocalFile() ? url.toLocalFile() : url.toString()); - QStringList headers; - QJsonArray elements; - - if (sourceFile.open(QFile::ReadOnly)) { - QTextStream stream(&sourceFile); - - if (!stream.atEnd()) - headers = stream.readLine().split(','); - - for (QString &header : headers) - header = header.trimmed(); - - if (!headers.isEmpty()) { - while (!stream.atEnd()) { - const QStringList recordDataList = stream.readLine().split(','); - int column = -1; - QJsonObject recordData; - for (const QString &cellData : recordDataList) { - if (++column == headers.size()) - break; - recordData.insert(headers.at(column), cellData); - } - elements.append(recordData); - } - } - } - - return elements; + static const QHash typeStringHash = CollectionDataTypeHelper::typeToStringHash(); + return typeStringHash.value(dataType); } -QString getFirstColumnName(const QString &collectionName) +CollectionDetails::DataType dataTypeFromString(const QString &dataType) { - Utils::FilePath dataStorePath = CollectionEditorUtils::dataStoreJsonFilePath(); - - if (!dataStorePath.exists()) - return {}; - - Utils::FileReader dataStoreFile; - if (!dataStoreFile.fetch(dataStorePath)) - return {}; - - QJsonParseError jsonError; - QJsonDocument dataStoreDocument = QJsonDocument::fromJson(dataStoreFile.data(), &jsonError); - if (jsonError.error == QJsonParseError::NoError) { - QJsonObject rootObject = dataStoreDocument.object(); - if (rootObject.contains(collectionName)) { - QJsonArray collectionArray = rootObject.value(collectionName).toArray(); - for (const QJsonValue &elementValue : std::as_const(collectionArray)) { - const QJsonObject elementObject = elementValue.toObject(); - QJsonObject::ConstIterator element = elementObject.constBegin(); - if (element != elementObject.constEnd()) - return element.key(); - } - } else { - qWarning() << Q_FUNC_INFO << __LINE__ - << QString("Collection \"%1\" not found.").arg(collectionName); - } - } else { - qWarning() << Q_FUNC_INFO << __LINE__ << "Problem in reading json file." - << jsonError.errorString(); - } - - return {}; + static const QHash stringTypeHash = CollectionDataTypeHelper::stringToTypeHash(); + return stringTypeHash.value(dataType, DataType::Unknown); } -bool collectionHasColumn(const QString &collectionName, const QString &columnName) +QStringList dataTypesStringList() { - Utils::FilePath dataStorePath = CollectionEditorUtils::dataStoreJsonFilePath(); - - if (!dataStorePath.exists()) - return false; - - Utils::FileReader dataStoreFile; - if (!dataStoreFile.fetch(dataStorePath)) - return false; - - QJsonParseError jsonError; - QJsonDocument dataStoreDocument = QJsonDocument::fromJson(dataStoreFile.data(), &jsonError); - if (jsonError.error == QJsonParseError::NoError) { - QJsonObject rootObject = dataStoreDocument.object(); - if (rootObject.contains(collectionName)) { - QJsonArray collectionArray = rootObject.value(collectionName).toArray(); - for (const QJsonValue &elementValue : std::as_const(collectionArray)) { - const QJsonObject elementObject = elementValue.toObject(); - QJsonObject::ConstIterator element = elementObject.constBegin(); - const QJsonObject::ConstIterator stopItem = elementObject.constEnd(); - - while (element != stopItem) { - const QString keyName = element.key(); - ++element; - - if (columnName == keyName) - return true; - } - } - } else { - qWarning() << Q_FUNC_INFO << __LINE__ - << QString("Collection \"%1\" not found.").arg(collectionName); - } - } else { - qWarning() << Q_FUNC_INFO << __LINE__ << "Problem in reading json file." - << jsonError.errorString(); - } - - return false; + static const QStringList typesList = CollectionDataTypeHelper::orderedTypeNames(); + return typesList; } } // namespace QmlDesigner::CollectionEditorUtils diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h index 46429f04b66..5f59329fc62 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h @@ -8,6 +8,7 @@ QT_BEGIN_NAMESPACE class QJsonArray; +class QJsonObject; QT_END_NAMESPACE namespace Utils { @@ -36,14 +37,12 @@ bool canAcceptCollectionAsModel(const ModelNode &node); bool hasTextRoleProperty(const ModelNode &node); -QJsonArray defaultCollectionArray(); +QJsonObject defaultCollection(); -QJsonArray loadAsSingleJsonCollection(const QUrl &url); +QString dataTypeToString(CollectionDetails::DataType dataType); -QJsonArray loadAsCsvCollection(const QUrl &url); +CollectionDetails::DataType dataTypeFromString(const QString &dataType); -QString getFirstColumnName(const QString &collectionName); - -bool collectionHasColumn(const QString &collectionName, const QString &columnName); +QStringList dataTypesStringList(); } // namespace QmlDesigner::CollectionEditorUtils diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.cpp index 1d27f205489..9378d6871c6 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.cpp @@ -270,7 +270,7 @@ bool CollectionSourceModel::collectionExists(const ModelNode &node, const QStrin bool CollectionSourceModel::addCollectionToSource(const ModelNode &node, const QString &collectionName, - const QJsonArray &newCollectionData, + const QJsonObject &newCollection, QString *errorString) { auto returnError = [errorString](const QString &msg) -> bool { @@ -308,7 +308,7 @@ bool CollectionSourceModel::addCollectionToSource(const ModelNode &node, if (document.isObject()) { QJsonObject sourceObject = document.object(); - sourceObject.insert(collectionName, newCollectionData); + sourceObject.insert(collectionName, newCollection); document.setObject(sourceObject); if (!jsonFile.resize(0)) return returnError(tr("Can't clean \"%1\".").arg(sourceFileInfo.absoluteFilePath())); diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.h b/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.h index 5ab77f2a98d..ac01c0de22b 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.h @@ -54,7 +54,7 @@ public: bool collectionExists(const ModelNode &node, const QString &collectionName) const; bool addCollectionToSource(const ModelNode &node, const QString &collectionName, - const QJsonArray &newCollectionData, + const QJsonObject &newCollection, QString *errorString = nullptr); ModelNode sourceNodeAt(int idx); diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp index 1b1d1a7d9fc..f2075b4abb9 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp @@ -226,7 +226,18 @@ void CollectionView::addResource(const QUrl &url, const QString &name, const QSt void CollectionView::assignCollectionToSelectedNode(const QString &collectionName) { QTC_ASSERT(dataStoreNode() && hasSingleSelectedModelNode(), return); - m_dataStore->assignCollectionToNode(this, singleSelectedModelNode(), collectionName); + m_dataStore->assignCollectionToNode( + this, + singleSelectedModelNode(), + collectionName, + [&](const QString &collectionName, const QString &columnName) -> bool { + const CollectionReference reference{dataStoreNode(), collectionName}; + return m_widget->collectionDetailsModel()->collectionHasColumn(reference, columnName); + }, + [&](const QString &collectionName) -> QString { + const CollectionReference reference{dataStoreNode(), collectionName}; + return m_widget->collectionDetailsModel()->getFirstColumnName(reference); + }); } void CollectionView::registerDeclarativeType() diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp index a123cf33617..efffa2c2e93 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp @@ -3,6 +3,7 @@ #include "collectionwidget.h" +#include "collectiondetails.h" #include "collectiondetailsmodel.h" #include "collectiondetailssortfiltermodel.h" #include "collectioneditorutils.h" @@ -215,7 +216,7 @@ bool CollectionWidget::addCollection(const QString &collectionName, if (collectionType == "json") { QJsonObject jsonObject; - jsonObject.insert(collectionName, CollectionEditorUtils::defaultCollectionArray()); + jsonObject.insert(collectionName, CollectionEditorUtils::defaultCollection()); QFile sourceFile(sourcePath); if (!sourceFile.open(QFile::WriteOnly)) { @@ -257,8 +258,10 @@ bool CollectionWidget::addCollection(const QString &collectionName, } } else if (collectionType == "json") { QString errorMsg; - bool added = m_sourceModel->addCollectionToSource( - node, collectionName, CollectionEditorUtils::defaultCollectionArray(), &errorMsg); + bool added = m_sourceModel->addCollectionToSource(node, + collectionName, + CollectionEditorUtils::defaultCollection(), + &errorMsg); if (!added) warn(tr("Can not add a model to the JSON file"), errorMsg); return added; @@ -267,50 +270,71 @@ bool CollectionWidget::addCollection(const QString &collectionName, return false; } -bool CollectionWidget::importToJson(const QVariant &sourceNode, - const QString &collectionName, - const QUrl &url) +bool CollectionWidget::importFile(const QString &collectionName, const QUrl &url) { - using CollectionEditorConstants::SourceFormat; using Utils::FilePath; - const ModelNode node = sourceNode.value(); - const SourceFormat nodeFormat = CollectionEditorUtils::getSourceCollectionFormat(node); - QTC_ASSERT(node.isValid() && nodeFormat == SourceFormat::Json, return false); + ensureDataStoreExists(); + + const ModelNode node = dataStoreNode(); + if (!node.isValid()) { + warn(tr("Can not import to the main model"), tr("The data store is not available.")); + return false; + } FilePath fileInfo = FilePath::fromUserInput(url.isLocalFile() ? url.toLocalFile() : url.toString()); bool added = false; QString errorMsg; - QJsonArray loadedCollection; + CollectionDetails loadedCollection; + QByteArray fileContent; - if (fileInfo.suffix() == "json") - loadedCollection = CollectionEditorUtils::loadAsSingleJsonCollection(url); - else if (fileInfo.suffix() == "csv") - loadedCollection = CollectionEditorUtils::loadAsCsvCollection(url); + auto loadUrlContent = [&]() -> bool { + QFile file(url.isLocalFile() ? url.toLocalFile() : url.toString()); - if (!loadedCollection.isEmpty()) { + if (file.open(QFile::ReadOnly)) { + fileContent = file.readAll(); + file.close(); + return true; + } + + warn(tr("Import from file"), tr("Cannot import from file \"%1\"").arg(file.fileName())); + return false; + }; + + if (fileInfo.suffix() == "json") { + if (!loadUrlContent()) + return false; + + QJsonParseError parseError; + QJsonDocument document = QJsonDocument::fromJson(fileContent, &parseError); + if (parseError.error != QJsonParseError::NoError) { + warn(tr("Json file Import error"), + tr("Cannot parse json content\n%1").arg(parseError.errorString())); + } + + loadedCollection = CollectionDetails::fromImportedJson(document); + } else if (fileInfo.suffix() == "csv") { + if (!loadUrlContent()) + return false; + loadedCollection = CollectionDetails::fromImportedCsv(fileContent); + } + + if (loadedCollection.columns()) { const QString newCollectionName = generateUniqueCollectionName(node, collectionName); - added = m_sourceModel->addCollectionToSource(node, newCollectionName, loadedCollection, &errorMsg); + added = m_sourceModel->addCollectionToSource(node, + newCollectionName, + loadedCollection.toLocalJson(), + &errorMsg); } else { errorMsg = tr("The imported model is empty or is not supported."); } if (!added) warn(tr("Can not add a model to the JSON file"), errorMsg); + return added; } -bool CollectionWidget::importCollectionToDataStore(const QString &collectionName, const QUrl &url) -{ - using Utils::FilePath; - const ModelNode node = dataStoreNode(); - if (node.isValid()) - return importToJson(QVariant::fromValue(node), collectionName, url); - - warn(tr("Can not import to the main model"), tr("The data store is not available.")); - return false; -} - bool CollectionWidget::addCollectionToDataStore(const QString &collectionName) { ensureDataStoreExists(); @@ -324,7 +348,7 @@ bool CollectionWidget::addCollectionToDataStore(const QString &collectionName) bool added = m_sourceModel->addCollectionToSource(node, generateUniqueCollectionName(node, collectionName), - CollectionEditorUtils::defaultCollectionArray(), + CollectionEditorUtils::defaultCollection(), &errorMsg); if (!added) warn(tr("Failed to add a model to the default model group"), errorMsg); diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h index 2be98df1901..5a9c45d591d 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h @@ -44,18 +44,10 @@ public: const QUrl &sourceUrl, const QVariant &sourceNode); - Q_INVOKABLE bool importToJson(const QVariant &sourceNode, - const QString &collectionName, - const QUrl &url); - - Q_INVOKABLE bool importCollectionToDataStore(const QString &collectionName, const QUrl &url); - + Q_INVOKABLE bool importFile(const QString &collectionName, const QUrl &url); Q_INVOKABLE bool addCollectionToDataStore(const QString &collectionName); - Q_INVOKABLE void assignCollectionToSelectedNode(const QString collectionName); - Q_INVOKABLE void ensureDataStoreExists(); - Q_INVOKABLE ModelNode dataStoreNode() const; void warn(const QString &title, const QString &body); diff --git a/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.cpp b/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.cpp index a1f82bbc65b..b85c6517857 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.cpp @@ -159,7 +159,9 @@ void DataStoreModelNode::reloadModel() reset(); } - QTC_ASSERT(m_model.get(), return); + if (!m_model.get()) + return; + m_model->setFileUrl(dataStoreQmlUrl); m_dataRelativePath = dataStoreJsonPath.relativePathFrom(dataStoreQmlPath).toFSPathString(); @@ -182,7 +184,8 @@ Model *DataStoreModelNode::model() const ModelNode DataStoreModelNode::modelNode() const { - QTC_ASSERT(m_model.get(), return {}); + if (!m_model.get()) + return {}; return m_model->rootModelNode(); } @@ -427,7 +430,9 @@ void DataStoreModelNode::removeCollection(const QString &collectionName) void DataStoreModelNode::assignCollectionToNode(AbstractView *view, const ModelNode &targetNode, - const QString &collectionName) + const QString &collectionName, + CollectionColumnFinder collectionHasColumn, + FirstColumnProvider firstColumnProvider) { QTC_ASSERT(targetNode.isValid(), return); @@ -461,14 +466,14 @@ void DataStoreModelNode::assignCollectionToNode(AbstractView *view, if (currentTextRoleValue.isValid() && !currentTextRoleValue.isNull()) { if (currentTextRoleValue.type() == QVariant::String) { const QString currentTextRole = currentTextRoleValue.toString(); - if (CollectionEditorUtils::collectionHasColumn(collectionName, currentTextRole)) + if (collectionHasColumn(collectionName, currentTextRole)) return; } else { return; } } - QString textRoleValue = CollectionEditorUtils::getFirstColumnName(collectionName); + QString textRoleValue = firstColumnProvider(collectionName); textRoleProperty.setValue(textRoleValue); } }); diff --git a/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.h b/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.h index 1c855bca7a2..d23908bc0c5 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.h +++ b/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.h @@ -18,6 +18,10 @@ class Model; class DataStoreModelNode { public: + using CollectionColumnFinder = std::function; + using FirstColumnProvider = std::function; + DataStoreModelNode(); void reloadModel(); @@ -32,7 +36,9 @@ public: void assignCollectionToNode(AbstractView *view, const ModelNode &targetNode, - const QString &collectionName); + const QString &collectionName, + CollectionColumnFinder collectionHasColumn, + FirstColumnProvider firstColumnProvider); private: QString getModelQmlText(); From 8b97598011b79152c5addc367f938556cefc7fb2 Mon Sep 17 00:00:00 2001 From: Knud Dollereder Date: Thu, 1 Feb 2024 15:17:58 +0100 Subject: [PATCH 020/176] QmlProjectManager: Add new cmake generator Automatic cmake generation can now be enabled by setting the qmlproject property enableCMakeGeneration to true Change-Id: I98523a9479d0cd812e43a9bd0b700120358260f6 Reviewed-by: Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen Reviewed-by: Burak Hancerli --- src/plugins/qmlprojectmanager/CMakeLists.txt | 3 +- .../buildsystem/projectitem/converters.cpp | 4 +- .../projectitem/qmlprojectitem.cpp | 12 + .../buildsystem/projectitem/qmlprojectitem.h | 3 + .../buildsystem/qmlbuildsystem.cpp | 10 + .../buildsystem/qmlbuildsystem.h | 4 + .../cmakegen/boilerplate.qrc | 3 + .../cmakegen/cmakegenerator.cpp | 550 ++++++++++++++++++ .../cmakegen/cmakegenerator.h | 101 ++++ .../cmakegen/gencmakeheadercomponents.tpl | 19 + .../cmakegen/gencmakemodule.tpl | 14 + .../cmakegen/gencmakeroot.tpl | 52 ++ .../converter/test-set-1/testfile.jsontoqml | 1 + .../converter/test-set-1/testfile.qmlproject | 2 + .../converter/test-set-1/testfile.qmltojson | 1 + .../converter/test-set-2/testfile.jsontoqml | 1 + .../converter/test-set-2/testfile.qmlproject | 2 + .../converter/test-set-2/testfile.qmltojson | 1 + .../converter/test-set-3/testfile.jsontoqml | 1 + .../converter/test-set-3/testfile.qmlproject | 2 + .../converter/test-set-3/testfile.qmltojson | 1 + .../test-set-mcu-1/testfile.jsontoqml | 1 + .../test-set-mcu-2/testfile.jsontoqml | 1 + .../data/getter-setter/empty.qmlproject | 1 + .../getter-setter/with_qds_prefix.qmlproject | 1 + .../without_qds_prefix.qmlproject | 1 + .../qmlprojectmanager/projectitem-test.cpp | 28 + 27 files changed, 818 insertions(+), 2 deletions(-) create mode 100644 src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.cpp create mode 100644 src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.h create mode 100644 src/plugins/qmlprojectmanager/cmakegen/gencmakeheadercomponents.tpl create mode 100644 src/plugins/qmlprojectmanager/cmakegen/gencmakemodule.tpl create mode 100644 src/plugins/qmlprojectmanager/cmakegen/gencmakeroot.tpl diff --git a/src/plugins/qmlprojectmanager/CMakeLists.txt b/src/plugins/qmlprojectmanager/CMakeLists.txt index a1212e8bc15..b6fa746996a 100644 --- a/src/plugins/qmlprojectmanager/CMakeLists.txt +++ b/src/plugins/qmlprojectmanager/CMakeLists.txt @@ -50,6 +50,7 @@ extend_qtc_plugin(QmlProjectManager cmakeprojectconverterdialog.cpp cmakeprojectconverterdialog.h generatecmakelists.cpp generatecmakelists.h generatecmakelistsconstants.h + cmakegenerator.cpp cmakegenerator.h boilerplate.qrc ) @@ -57,7 +58,7 @@ add_qtc_library(QmlProjectManagerLib OBJECT CONDITION Qt6_VERSION VERSION_GREATER_EQUAL 6.4.3 EXCLUDE_FROM_INSTALL DEPENDS - QmlJS Utils + QmlJS Utils ProjectExplorer INCLUDES ${CMAKE_CURRENT_LIST_DIR} SOURCES_PREFIX ${CMAKE_CURRENT_LIST_DIR}/buildsystem diff --git a/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp b/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp index b35679ed348..08fba2c0981 100644 --- a/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp +++ b/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp @@ -143,6 +143,7 @@ QString jsonToQmlProject(const QJsonObject &rootObject) appendString("mainFile", runConfig["mainFile"].toString()); appendString("mainUiFile", runConfig["mainUiFile"].toString()); appendString("targetDirectory", deploymentConfig["targetDirectory"].toString()); + appendBool("enableCMakeGeneration", deploymentConfig["enableCMakeGeneration"].toBool()); appendBool("widgetApp", runConfig["widgetApp"].toBool()); appendStringArray("importPaths", rootObject["importPaths"].toVariant().toStringList()); appendBreak(); @@ -282,7 +283,8 @@ QJsonObject qmlProjectTojson(const Utils::FilePath &projectFile) || propName.contains("mainuifile", Qt::CaseInsensitive) || propName.contains("forcefreetype", Qt::CaseInsensitive)) { currentObj = &runConfigObject; - } else if (propName.contains("targetdirectory", Qt::CaseInsensitive)) { + } else if (propName.contains("targetdirectory", Qt::CaseInsensitive) + || propName.contains("enableCMakeGeneration", Qt::CaseInsensitive)) { currentObj = &deploymentObject; } else if (propName.contains("qtformcus", Qt::CaseInsensitive)) { qtForMCUs = value.toBool(); diff --git a/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.cpp b/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.cpp index 77d0eea82d5..5524e927e5f 100644 --- a/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.cpp +++ b/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.cpp @@ -422,4 +422,16 @@ void QmlProjectItem::insertAndUpdateProjectFile(const QString &key, const QJsonV m_projectFile.writeFileContents(Converters::jsonToQmlProject(m_project).toUtf8()); } +bool QmlProjectItem::enableCMakeGeneration() const +{ + return m_project["deployment"].toObject()["enableCMakeGeneration"].toBool(); +} + +void QmlProjectItem::setEnableCMakeGeneration(bool enable) +{ + QJsonObject obj = m_project["deployment"].toObject(); + obj["enableCMakeGeneration"] = enable; + insertAndUpdateProjectFile("deployment", obj); +} + } // namespace QmlProjectManager diff --git a/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.h b/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.h index 214614f5365..7d57ad2e60d 100644 --- a/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.h +++ b/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.h @@ -88,6 +88,9 @@ public: QJsonObject project() const; + bool enableCMakeGeneration() const; + void setEnableCMakeGeneration(bool enable); + signals: void qmlFilesChanged(const QSet &, const QSet &); diff --git a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp index 5131d9c57cc..e91fd72baa9 100644 --- a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp +++ b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp @@ -77,6 +77,7 @@ void updateMcuBuildStep(Target *target, bool mcuEnabled) QmlBuildSystem::QmlBuildSystem(Target *target) : BuildSystem(target) + , m_cmakeGen(new GenerateCmake::CMakeGenerator(this, this)) { // refresh first - project information is used e.g. to decide the default RC's refresh(RefreshOptions::Project); @@ -86,10 +87,12 @@ QmlBuildSystem::QmlBuildSystem(Target *target) connect(target->project(), &Project::activeTargetChanged, this, [this](Target *target) { refresh(RefreshOptions::NoFileRefresh); + m_cmakeGen->initialize(qmlProject()); updateMcuBuildStep(target, qtForMCUs()); }); connect(target->project(), &Project::projectFileIsDirty, this, [this]() { refresh(RefreshOptions::Project); + m_cmakeGen->initialize(qmlProject()); updateMcuBuildStep(project()->activeTarget(), qtForMCUs()); }); @@ -220,6 +223,13 @@ void QmlBuildSystem::initProjectItem() &QmlProjectItem::qmlFilesChanged, this, &QmlBuildSystem::refreshFiles); + + connect(m_projectItem.get(), + &QmlProjectItem::qmlFilesChanged, + m_cmakeGen, + &GenerateCmake::CMakeGenerator::update); + + m_cmakeGen->setEnabled(m_projectItem->enableCMakeGeneration()); } void QmlBuildSystem::parseProjectFiles() diff --git a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.h b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.h index 3615979e2df..524af5cd1bb 100644 --- a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.h +++ b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.h @@ -8,6 +8,8 @@ #include "../qmlprojectmanager_global.h" #include +#include "qmlprojectmanager/cmakegen/cmakegenerator.h" + namespace QmlProjectManager { class QmlProject; @@ -122,6 +124,8 @@ private: void registerMenuButtons(); void updateDeploymentData(); friend class FilesUpdateBlocker; + + GenerateCmake::CMakeGenerator* m_cmakeGen; }; } // namespace QmlProjectManager diff --git a/src/plugins/qmlprojectmanager/cmakegen/boilerplate.qrc b/src/plugins/qmlprojectmanager/cmakegen/boilerplate.qrc index a89643d6fff..10fab598382 100644 --- a/src/plugins/qmlprojectmanager/cmakegen/boilerplate.qrc +++ b/src/plugins/qmlprojectmanager/cmakegen/boilerplate.qrc @@ -1,5 +1,8 @@ + gencmakeroot.tpl + gencmakemodule.tpl + gencmakeheadercomponents.tpl qmlprojectmaincpp.tpl qmlprojectmaincppheader.tpl qmlprojectenvheader.tpl diff --git a/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.cpp b/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.cpp new file mode 100644 index 00000000000..55bcea6a87d --- /dev/null +++ b/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.cpp @@ -0,0 +1,550 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "cmakegenerator.h" +#include "generatecmakelistsconstants.h" + +#include "projectexplorer/projectmanager.h" +#include "projectexplorer/projectnodes.h" +#include "qmlprojectmanager/qmlproject.h" + +#include + +#include + +namespace QmlProjectManager { + +namespace GenerateCmake { + +const char TEMPLATE_CMAKELISTS_ROOT[] = ":/boilerplatetemplates/gencmakeroot.tpl"; +const char TEMPLATE_CMAKELISTS_MODULE[] = ":/boilerplatetemplates/gencmakemodule.tpl"; + +const char TEMPLATE_QMLMODULES[] = ":/boilerplatetemplates/qmlprojectmodules.tpl"; +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"; + +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"; + +const char BIG_RESOURCE_TEMPLATE[] = R"( +qt6_add_resources(%1 %2 + BIG_RESOURCES + PREFIX "%3" + VERSION 1.0 + FILES %4 +))"; + +CMakeGenerator::CMakeGenerator(QmlBuildSystem *bs, QObject *parent) + : QObject(parent) + , m_root(std::make_shared()) + , m_buildSystem(bs) +{} + +void CMakeGenerator::setEnabled(bool enabled) +{ + m_enabled = enabled; +} + +void CMakeGenerator::initialize(QmlProject *project) +{ + if (!m_enabled) + return; + + m_srcs.clear(); + m_moduleNames.clear(); + + m_root = std::make_shared(); + m_root->name = QString("Root"); + m_root->dir = project->rootProjectDirectory(); + + m_projectName = project->displayName(); + + ProjectExplorer::ProjectNode *rootProjectNode = project->rootProjectNode(); + parseNodeTree(m_root, rootProjectNode); + parseSourceTree(); + + createCMakeFiles(m_root); + createEntryPoints(m_root); +} + +void CMakeGenerator::update(const QSet &added, const QSet &removed) +{ + if (!m_enabled) + return; + + std::set dirtyModules; + for (const QString &add : added) { + const Utils::FilePath path = Utils::FilePath::fromString(add); + if (auto node = findOrCreateNode(m_root, path)) { + insertFile(node, path); + if (auto module = findModuleFor(node)) + dirtyModules.insert(module); + } else { + qDebug() << "CmakeGen: Failed to find Folder node " << path; + } + } + + for (const QString &remove : removed) { + const Utils::FilePath path = Utils::FilePath::fromString(remove); + if (auto node = findNode(m_root, path)) { + removeFile(node, path); + if (auto module = findModuleFor(node)) + dirtyModules.insert(module); + } + } + + for (auto module : dirtyModules) + createModuleCMakeFile(module); +} + +std::vector CMakeGenerator::qmlFiles(const NodePtr &node) const +{ + std::vector out = node->files; + for (const NodePtr &child : node->subdirs) { + if (child->module) + continue; + + auto childFiles = qmlFiles(child); + out.insert(out.end(), childFiles.begin(), childFiles.end()); + } + return out; +} + +std::vector CMakeGenerator::singletons(const NodePtr &node) const +{ + std::vector out = node->singletons; + for (const NodePtr &child : node->subdirs) { + if (child->module) + continue; + + auto childFiles = singletons(child); + out.insert(out.end(), childFiles.begin(), childFiles.end()); + } + return out; +} + +std::vector CMakeGenerator::resources(const NodePtr &node) const +{ + std::vector out = node->resources; + for (const NodePtr &child : node->subdirs) { + if (child->module) + continue; + + auto childFiles = resources(child); + out.insert(out.end(), childFiles.begin(), childFiles.end()); + } + return out; +} + +std::vector CMakeGenerator::sources(const NodePtr &node) const +{ + std::vector out = node->sources; + for (const NodePtr &child : node->subdirs) { + if (child->module) + continue; + + auto childFiles = sources(child); + out.insert(out.end(), childFiles.begin(), childFiles.end()); + } + return out; +} + +void CMakeGenerator::createCMakeFiles(const NodePtr &node) const +{ + if (node->name == "Root") { + createMainCMakeFile(node); + createQmlModuleFile(node); + } else if (node->module || hasChildModule(node)) { + createModuleCMakeFile(node); + } + 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::createQmlModuleFile(const NodePtr &node) const +{ + const QString appName = m_projectName + "App"; + + QString subdirIncludes; + for (const NodePtr &n : node->subdirs) + subdirIncludes.append(QString(ADD_SUBDIR).arg(n->name)); + + QString modulesAsPlugins; + for (const QString &moduleName : m_moduleNames) + modulesAsPlugins.append(" " + moduleName + "plugin\n"); + + const QString fileTemplate = readTemplate(TEMPLATE_QMLMODULES); + const QString fileContent = fileTemplate.arg(appName, subdirIncludes, modulesAsPlugins); + + const Utils::FilePath file = node->dir.pathAppended("qmlModules"); + writeFile(file, fileContent); +} + +void CMakeGenerator::createModuleCMakeFile(const NodePtr &node) const +{ + QString subDirContent; + for (const NodePtr &n : node->subdirs) { + if (n->module || hasChildModule(n)) + subDirContent.append(QString(ADD_SUBDIR).arg(n->dir.fileName())); + } + + QString content; + if (!node->module && hasChildModule(node)) { + content.append(DO_NOT_EDIT_FILE_COMMENT); + content.append(subDirContent); + Utils::FilePath file = node->dir.pathAppended("CMakeLists.txt"); + writeFile(file, content); + return; + } + + auto makeRelative = [](const Utils::FilePath &base, + const Utils::FilePath &converted) -> QString { + return "\"" + Utils::FilePath::calcRelativePath(converted.toString(), base.toString()) + "\""; + }; + + QString uri = node->uri; + if (uri.isEmpty()) + uri = node->dir.baseName(); + + const QString setProperties( + "set_source_files_properties(%1\n PROPERTIES\n %2 %3\n)\n\n"); + + for (const Utils::FilePath &path : node->singletons) { + content.append(setProperties.arg(path.fileName()).arg("QT_QML_SINGLETON_TYPE").arg("true")); + } + + if (!subDirContent.isEmpty()) + content.append(subDirContent); + + QString qmlFileContent; + for (const Utils::FilePath &path : qmlFiles(node)) { + qmlFileContent.append(QString(" %1\n").arg(makeRelative(node->dir, path))); + } + + QString moduleContent; + if (!qmlFileContent.isEmpty()) + moduleContent.append(QString(" QML_FILES\n%1").arg(qmlFileContent)); + + std::vector bigResources; + QString resourceFiles; + for (const Utils::FilePath &path : resources(node)) { + if (path.fileSize() > 5000000) { + bigResources.push_back(makeRelative(node->dir, path)); + continue; + } + resourceFiles.append(QString(" %1\n").arg(makeRelative(node->dir, path))); + } + + if (!resourceFiles.isEmpty()) + moduleContent.append(QString(" RESOURCES\n%1").arg(resourceFiles)); + + QString bigResourceContent; + if (!bigResources.empty()) { + QString resourceContent; + for (const QString &res : bigResources) + resourceContent.append(QString("\n %1").arg(res)); + + const QString prefixPath = QString(uri).replace('.', '/'); + const QString prefix = "/qt/qml/" + prefixPath; + const QString resourceName = node->name + "BigResource"; + + bigResourceContent = QString::fromUtf8(BIG_RESOURCE_TEMPLATE, -1) + .arg(node->name, resourceName, prefix, resourceContent); + } + + const QString fileTemplate = readTemplate(TEMPLATE_CMAKELISTS_MODULE); + const QString fileContent = + fileTemplate.arg(content, node->name, uri, moduleContent, bigResourceContent); + + const Utils::FilePath file = node->dir.pathAppended("CMakeLists.txt"); + writeFile(file, 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::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::NameValueItem &item) { return item.name == key; }); + if (confEnv != envItems.end()) + value = confEnv->value; + } + return value; +} + +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; +} + +void CMakeGenerator::readQmlDir(const Utils::FilePath &filePath, NodePtr &node) const +{ + node->module = true; + + QFile f(filePath.toString()); + f.open(QIODevice::ReadOnly); + QTextStream stream(&f); + + Utils::FilePath dir = filePath.parentDir(); + while (!stream.atEnd()) { + const QString line = stream.readLine(); + const QStringList tokenizedLine = line.split(QRegularExpression("\\s+")); + const QString maybeFileName = tokenizedLine.last(); + if (tokenizedLine.first().compare("module", Qt::CaseInsensitive) == 0) { + node->uri = tokenizedLine.last(); + node->name = QString(node->uri).replace('.', '_'); + } else if (maybeFileName.endsWith(".qml", Qt::CaseInsensitive)) { + Utils::FilePath tmp = dir.pathAppended(maybeFileName); + if (tokenizedLine.first() == "singleton") + node->singletons.push_back(tmp); + } + } + + f.close(); +} + +CMakeGenerator::NodePtr CMakeGenerator::findModuleFor(const NodePtr &node) const +{ + NodePtr current = node; + while (current->parent) { + if (current->module) + return current; + + current = current->parent; + } + return nullptr; +} + +CMakeGenerator::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) + return child; + if (path.isChildOf(child->dir)) + return findNode(child, path); + } + return nullptr; +} + +CMakeGenerator::NodePtr CMakeGenerator::findOrCreateNode(NodePtr &node, + const Utils::FilePath &path) const +{ + if (auto found = findNode(node, path)) + return found; + + if (!path.isChildOf(node->dir)) + return nullptr; + + const Utils::FilePath parentDir = path.parentDir(); + const Utils::FilePath relative = parentDir.relativeChildPath(node->dir); + const QChar separator = relative.pathComponentSeparator(); + const QList components = relative.pathView().split(separator); + + NodePtr last = node; + for (const auto &comp : components) { + NodePtr newNode = std::make_shared(); + newNode->parent = last; + newNode->name = comp.toString(); + newNode->dir = last->dir.pathAppended(comp.toString()); + last->subdirs.push_back(newNode); + last = newNode; + } + return last; +} + +void CMakeGenerator::insertFile(NodePtr &node, const Utils::FilePath &path) const +{ + 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 (isResource(path)) { + node->resources.push_back(path); + } +} + +void CMakeGenerator::removeFile(NodePtr &node, const Utils::FilePath &path) const +{ + if (path.fileName() == "qmldir") { + node->module = false; + node->singletons.clear(); + node->uri = ""; + node->name = path.parentDir().fileName(); + + } else if (path.suffix() == "qml") { + auto iter = std::find(node->files.begin(), node->files.end(), path); + if (iter != node->files.end()) + node->files.erase(iter); + } else if (isResource(path)) { + auto iter = std::find(node->resources.begin(), node->resources.end(), path); + if (iter != node->resources.end()) + node->resources.erase(iter); + } +} + +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::printNodeTree(const NodePtr &generatorNode, size_t indent) const +{ + auto addIndent = [](size_t level) -> QString { + QString str; + for (size_t i = 0; i < level; ++i) + str += " "; + return str; + }; + + qDebug() << addIndent(indent) << "GeneratorNode: " << generatorNode->name; + qDebug() << addIndent(indent) << "directory: " << generatorNode->dir; + qDebug() << addIndent(indent) << "files: " << generatorNode->files; + qDebug() << addIndent(indent) << "singletons: " << generatorNode->singletons; + qDebug() << addIndent(indent) << "resources: " << generatorNode->resources; + qDebug() << addIndent(indent) << "sources: " << generatorNode->sources; + + for (const auto &child : generatorNode->subdirs) + printNodeTree(child, indent + 1); +} + +void CMakeGenerator::parseNodeTree(NodePtr &generatorNode, + const ProjectExplorer::FolderNode *folderNode) +{ + for (const auto *childNode : folderNode->nodes()) { + if (const auto *subFolderNode = childNode->asFolderNode()) { + CMakeGenerator::NodePtr childGeneratorNode = std::make_shared(); + childGeneratorNode->parent = generatorNode; + childGeneratorNode->name = subFolderNode->displayName(); + childGeneratorNode->dir = subFolderNode->filePath(); + parseNodeTree(childGeneratorNode, subFolderNode); + generatorNode->subdirs.push_back(childGeneratorNode); + } else if (auto *fileNode = childNode->asFileNode()) { + insertFile(generatorNode, fileNode->filePath()); + } + } + + if (generatorNode->name == "content") + generatorNode->module = true; + + if (generatorNode->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); + while (it.hasNext()) { + QString relative = Utils::FilePath::calcRelativePath(it.next(), m_root->dir.path()); + m_srcs.push_back(relative); + } + + if (m_srcs.empty()) + m_srcs.push_back("src/main.cpp"); +} + +} // namespace GenerateCmake +} // namespace QmlProjectManager diff --git a/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.h b/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.h new file mode 100644 index 00000000000..54445d3561d --- /dev/null +++ b/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.h @@ -0,0 +1,101 @@ +// Copyright (C) 2023 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 + +namespace ProjectExplorer { +class FolderNode; +} + +namespace QmlProjectManager { + +class QmlProject; +class QmlBuildSystem; + +namespace GenerateCmake { + +// TODO: +// - Create "module" for src dir +// - Replace AppName in templates with ${CMAKE_PROJECT_NAME} +// - Introduce Blacklist (designer) + +class CMakeGenerator : public QObject +{ + Q_OBJECT + +public: + CMakeGenerator(QmlBuildSystem *bs, QObject *parent = nullptr); + + void setEnabled(bool enabled); + + void initialize(QmlProject *project); + + void update(const QSet &added, const QSet &removed); + +private: + struct Node + { + std::shared_ptr parent = nullptr; + bool module = false; + + QString uri; + QString name; + Utils::FilePath dir; + + std::vector> subdirs; + std::vector files; + std::vector singletons; + std::vector resources; + std::vector sources; + }; + + using NodePtr = std::shared_ptr; + + std::vector qmlFiles(const NodePtr &node) const; + std::vector singletons(const NodePtr &node) const; + std::vector resources(const NodePtr &node) const; + std::vector sources(const NodePtr &node) const; + + void createCMakeFiles(const NodePtr &node) const; + + void createQmlModuleFile(const NodePtr &node) const; + void createMainCMakeFile(const NodePtr &node) const; + void createModuleCMakeFile(const NodePtr &node) const; + + void createEntryPoints(const NodePtr &node) const; + void createMainCppFile(const NodePtr &node) const; + void writeFile(const Utils::FilePath &path, const QString &content) const; + + QString makeEnvironmentVariable(const QString &key) 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; + + void insertFile(NodePtr &node, const Utils::FilePath &path) const; + void removeFile(NodePtr &node, const Utils::FilePath &path) const; + + bool hasChildModule(const NodePtr &node) const; + bool isResource(const Utils::FilePath &path) const; + + void printNodeTree(const NodePtr &generatorNode, size_t indent = 0) const; + + void parseNodeTree(NodePtr &generatorNode, const ProjectExplorer::FolderNode *folderNode); + void parseSourceTree(); + + bool m_enabled = false; + QString m_projectName = {}; + NodePtr m_root = {}; + QStringList m_srcs = {}; + std::vector m_moduleNames = {}; + QmlBuildSystem *m_buildSystem = nullptr; +}; + +} // namespace GenerateCmake +} // namespace QmlProjectManager diff --git a/src/plugins/qmlprojectmanager/cmakegen/gencmakeheadercomponents.tpl b/src/plugins/qmlprojectmanager/cmakegen/gencmakeheadercomponents.tpl new file mode 100644 index 00000000000..167481d7c74 --- /dev/null +++ b/src/plugins/qmlprojectmanager/cmakegen/gencmakeheadercomponents.tpl @@ -0,0 +1,19 @@ +/* + * This file is automatically generated by Qt Design Studio. + * Do not change. +*/ + +#include "qqmlextensionplugin.h" + +#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 diff --git a/src/plugins/qmlprojectmanager/cmakegen/gencmakemodule.tpl b/src/plugins/qmlprojectmanager/cmakegen/gencmakemodule.tpl new file mode 100644 index 00000000000..7c4b38b0433 --- /dev/null +++ b/src/plugins/qmlprojectmanager/cmakegen/gencmakemodule.tpl @@ -0,0 +1,14 @@ +### 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 +) + +%5 diff --git a/src/plugins/qmlprojectmanager/cmakegen/gencmakeroot.tpl b/src/plugins/qmlprojectmanager/cmakegen/gencmakeroot.tpl new file mode 100644 index 00000000000..0b8553d1958 --- /dev/null +++ b/src/plugins/qmlprojectmanager/cmakegen/gencmakeroot.tpl @@ -0,0 +1,52 @@ +cmake_minimum_required(VERSION 3.21.1) + +option(LINK_INSIGHT "Link Qt Insight Tracker library" ON) +option(BUILD_QDS_COMPONENTS "Build design studio components" ON) + +project(%1 LANGUAGES CXX) + +set(CMAKE_AUTOMOC ON) +set(CMAKE_INCLUDE_CURRENT_DIR ON) +set(QT_QML_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/qml) +set(QML_IMPORT_PATH ${QT_QML_OUTPUT_DIRECTORY} + CACHE STRING "Import paths for Qt Creator's code model" + FORCE +) + +find_package(Qt6 6.2 REQUIRED COMPONENTS Core Gui Qml Quick) + +if (Qt6_VERSION VERSION_GREATER_EQUAL 6.3) + qt_standard_project_setup() +endif() + +qt_add_executable(%1 %2) + +qt_add_resources(%1 "configuration" + PREFIX "/" +%3 +) + +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 +) + +if (BUILD_QDS_COMPONENTS) + include(${CMAKE_CURRENT_SOURCE_DIR}/qmlcomponents OPTIONAL) +endif() + +include(${CMAKE_CURRENT_SOURCE_DIR}/qmlmodules) + +if (LINK_INSIGHT) + include(${CMAKE_CURRENT_SOURCE_DIR}/insight OPTIONAL) +endif () + +include(GNUInstallDirs) +install(TARGETS %1 + BUNDLE DESTINATION . + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} +) + diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-1/testfile.jsontoqml b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-1/testfile.jsontoqml index 2a67b12d505..90c980cda1b 100644 --- a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-1/testfile.jsontoqml +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-1/testfile.jsontoqml @@ -7,6 +7,7 @@ Project { mainFile: "content/App.qml" mainUiFile: "content/Screen01.ui.qml" targetDirectory: "/opt/UntitledProject13" + enableCMakeGeneration: false widgetApp: true importPaths: [ "imports","asset_imports" ] diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-1/testfile.qmlproject b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-1/testfile.qmlproject index 260938164a7..cb0ca5f9e10 100644 --- a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-1/testfile.qmlproject +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-1/testfile.qmlproject @@ -90,6 +90,8 @@ Project { /* Required for deployment */ targetDirectory: "/opt/UntitledProject13" + enableCMakeGeneration: false + qdsVersion: "4.0" quickVersion: "6.2" diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-1/testfile.qmltojson b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-1/testfile.qmltojson index c4475af39c4..815e9a0cc56 100644 --- a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-1/testfile.qmltojson +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-1/testfile.qmltojson @@ -1,5 +1,6 @@ { "deployment": { + "enableCMakeGeneration": false, "targetDirectory": "/opt/UntitledProject13" }, "environment": { diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-2/testfile.jsontoqml b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-2/testfile.jsontoqml index 5878bdafc73..16617e015dc 100644 --- a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-2/testfile.jsontoqml +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-2/testfile.jsontoqml @@ -6,6 +6,7 @@ import QmlProject Project { mainFile: "fileSelectors.qml" targetDirectory: "/opt/fileSelectors" + enableCMakeGeneration: false widgetApp: false importPaths: [ "imports" ] diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-2/testfile.qmlproject b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-2/testfile.qmlproject index 3ceeda651af..29bc108e682 100644 --- a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-2/testfile.qmlproject +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-2/testfile.qmlproject @@ -44,4 +44,6 @@ Project { /* Required for deployment */ targetDirectory: "/opt/fileSelectors" + + enableCMakeGeneration: false } diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-2/testfile.qmltojson b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-2/testfile.qmltojson index a26e0fc1607..2231f36ff21 100644 --- a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-2/testfile.qmltojson +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-2/testfile.qmltojson @@ -1,5 +1,6 @@ { "deployment": { + "enableCMakeGeneration": false, "targetDirectory": "/opt/fileSelectors" }, "environment": { diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-3/testfile.jsontoqml b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-3/testfile.jsontoqml index 2a67b12d505..90c980cda1b 100644 --- a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-3/testfile.jsontoqml +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-3/testfile.jsontoqml @@ -7,6 +7,7 @@ Project { mainFile: "content/App.qml" mainUiFile: "content/Screen01.ui.qml" targetDirectory: "/opt/UntitledProject13" + enableCMakeGeneration: false widgetApp: true importPaths: [ "imports","asset_imports" ] diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-3/testfile.qmlproject b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-3/testfile.qmlproject index 1ec43b95d5a..faf115d609c 100644 --- a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-3/testfile.qmlproject +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-3/testfile.qmlproject @@ -90,6 +90,8 @@ Project { /* Required for deployment */ QDS.targetDirectory: "/opt/UntitledProject13" + QDS.enableCMakeGeneration: false + QDS.qdsVersion: "4.0" QDS.quickVersion: "6.2" diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-3/testfile.qmltojson b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-3/testfile.qmltojson index c4475af39c4..815e9a0cc56 100644 --- a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-3/testfile.qmltojson +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-3/testfile.qmltojson @@ -1,5 +1,6 @@ { "deployment": { + "enableCMakeGeneration": false, "targetDirectory": "/opt/UntitledProject13" }, "environment": { diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-mcu-1/testfile.jsontoqml b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-mcu-1/testfile.jsontoqml index 99e4f60bb34..04911c40f89 100644 --- a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-mcu-1/testfile.jsontoqml +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-mcu-1/testfile.jsontoqml @@ -6,6 +6,7 @@ import QmlProject Project { mainFile: "Main.qml" targetDirectory: "/opt/UntitledProject13" + enableCMakeGeneration: false widgetApp: true importPaths: [ "imports","asset_imports" ] diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-mcu-2/testfile.jsontoqml b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-mcu-2/testfile.jsontoqml index 2e73146cdaf..ad0102201fc 100644 --- a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-mcu-2/testfile.jsontoqml +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-mcu-2/testfile.jsontoqml @@ -4,6 +4,7 @@ import QmlProject Project { + enableCMakeGeneration: false widgetApp: false qt6Project: false diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/getter-setter/empty.qmlproject b/tests/unit/tests/unittests/qmlprojectmanager/data/getter-setter/empty.qmlproject index 66adaaa7d91..bcf8995b7ac 100644 --- a/tests/unit/tests/unittests/qmlprojectmanager/data/getter-setter/empty.qmlproject +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/getter-setter/empty.qmlproject @@ -11,6 +11,7 @@ Project { importPaths: [ ] targetDirectory: "" + enableCMakeGeneration: false fileSelectors: [ ] qdsVersion: "" diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/getter-setter/with_qds_prefix.qmlproject b/tests/unit/tests/unittests/qmlprojectmanager/data/getter-setter/with_qds_prefix.qmlproject index a8b5b459d65..6422179e668 100644 --- a/tests/unit/tests/unittests/qmlprojectmanager/data/getter-setter/with_qds_prefix.qmlproject +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/getter-setter/with_qds_prefix.qmlproject @@ -11,6 +11,7 @@ Project { QDS.importPaths: [ "imports", "asset_imports" ] QDS.targetDirectory: "/opt/targetDirectory" + QDS.enableCMakeGeneration: true QDS.fileSelectors: [ "WXGA", "darkTheme", "ShowIndicator"] QDS.qdsVersion: "3.9" diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/getter-setter/without_qds_prefix.qmlproject b/tests/unit/tests/unittests/qmlprojectmanager/data/getter-setter/without_qds_prefix.qmlproject index 6bee5999555..77b55568937 100644 --- a/tests/unit/tests/unittests/qmlprojectmanager/data/getter-setter/without_qds_prefix.qmlproject +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/getter-setter/without_qds_prefix.qmlproject @@ -11,6 +11,7 @@ Project { importPaths: [ "imports", "asset_imports" ] targetDirectory: "/opt/targetDirectory" + enableCMakeGeneration: true fileSelectors: [ "WXGA", "darkTheme", "ShowIndicator"] qdsVersion: "3.9" diff --git a/tests/unit/tests/unittests/qmlprojectmanager/projectitem-test.cpp b/tests/unit/tests/unittests/qmlprojectmanager/projectitem-test.cpp index 49caf960746..1e1d7d1189c 100644 --- a/tests/unit/tests/unittests/qmlprojectmanager/projectitem-test.cpp +++ b/tests/unit/tests/unittests/qmlprojectmanager/projectitem-test.cpp @@ -121,6 +121,13 @@ TEST_F(QmlProjectItem, get_with_qds_prefix_tar_get_with_qds_prefix_directory) ASSERT_THAT(targetDirectory, Eq("/opt/targetDirectory")); } +TEST_F(QmlProjectItem, get_with_qds_prefix_enable_cmake_generation) +{ + auto enable = projectItemWithQdsPrefix->enableCMakeGeneration(); + + ASSERT_TRUE(enable); +} + TEST_F(QmlProjectItem, get_with_qds_prefix_import_paths) { auto importPaths = projectItemWithQdsPrefix->importPaths(); @@ -266,6 +273,13 @@ TEST_F(QmlProjectItem, get_without_qds_prefix_tar_get_without_qds_prefix_directo ASSERT_THAT(targetDirectory, Eq("/opt/targetDirectory")); } +TEST_F(QmlProjectItem, get_without_qds_prefix_enable_cmake_generation) +{ + auto enable = projectItemWithoutQdsPrefix->enableCMakeGeneration(); + + ASSERT_TRUE(enable); +} + TEST_F(QmlProjectItem, get_without_qds_prefix_import_paths) { auto importPaths = projectItemWithoutQdsPrefix->importPaths(); @@ -413,6 +427,13 @@ TEST_F(QmlProjectItem, get_empty_tar_get_empty_directory) ASSERT_THAT(targetDirectory, IsEmpty()); } +TEST_F(QmlProjectItem, get_empty_enable_cmake_generation) +{ + auto enable = projectItemEmpty->enableCMakeGeneration(); + + ASSERT_FALSE(enable); +} + TEST_F(QmlProjectItem, get_empty_import_paths) { auto importPaths = projectItemEmpty->importPaths(); @@ -677,6 +698,13 @@ TEST_F(QmlProjectItem, set_design_studio_version) ASSERT_EQ(projectItemSetters->versionDesignStudio(), "6"); } +TEST_F(QmlProjectItem, set_enable_cmake_generation) +{ + projectItemSetters->setEnableCMakeGeneration(true); + + ASSERT_EQ(projectItemSetters->enableCMakeGeneration(), true); +} + // TODO: We should move these 2 tests into the integration tests TEST_F(QmlProjectItem, test_file_filters) { From c5f86ea0b5782797a0746bfc3e849613a0bd6ebd Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Tue, 6 Feb 2024 13:30:30 +0200 Subject: [PATCH 021/176] QmlDesigner: Implement effect item visibility handling Now effects made with effect composer can be hidden/shown using visible property. Fixes: QDS-11786 Change-Id: I44782246adfa3ba3cd0a8203fa67b3f5412535f7 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri --- .../effectcomposer/effectcomposermodel.cpp | 27 +++++++++++--- .../components/formeditor/formeditorview.cpp | 7 +++- .../designercore/include/qmlitemnode.h | 1 + .../designercore/model/qmlitemnode.cpp | 5 +++ .../instances/qt5rendernodeinstanceserver.cpp | 35 +++++++++++++++++++ .../instances/qt5rendernodeinstanceserver.h | 3 ++ 6 files changed, 73 insertions(+), 5 deletions(-) diff --git a/src/plugins/effectcomposer/effectcomposermodel.cpp b/src/plugins/effectcomposer/effectcomposermodel.cpp index 9919fc7a308..00d51a954a6 100644 --- a/src/plugins/effectcomposer/effectcomposermodel.cpp +++ b/src/plugins/effectcomposer/effectcomposermodel.cpp @@ -583,9 +583,13 @@ import QtQuick Item { id: rootItem - // This is an internal property used by tooling to identify effect items + // Use visible property to show and hide the effect. + visible: true + + // This is an internal property used by tooling to identify effect items. Do not modify. property var _isEffectItem + // This is an internal property used to manage the effect. Do not modify. property Item _oldParent: null )" }; @@ -593,7 +597,7 @@ Item { s += header.arg(qApp->applicationVersion(), QDateTime::currentDateTime().toString()); if (m_shaderFeatures.enabled(ShaderFeatures::Source)) { - s += " // This is the main source for the effect\n"; + 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"; } if (m_shaderFeatures.enabled(ShaderFeatures::Time) @@ -622,11 +626,26 @@ R"( } if (parent) { _oldParent = parent - parent.layer.enabled = true - parent.layer.effect = effectComponent + if (visible) { + parent.layer.enabled = true + parent.layer.effect = effectComponent + } %1 } } + + onVisibleChanged: { + if (visible) { + parent.layer.enabled = true + parent.layer.effect = effectComponent + source = parent + } else { + parent.layer.enabled = false + parent.layer.effect = null + source = null + } + parent.update() + } )" }; diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp b/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp index 99d243867f7..9adc275236b 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp +++ b/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp @@ -863,6 +863,7 @@ QmlItemNode findRecursiveQmlItemNode(const QmlObjectNode &firstQmlObjectNode) void FormEditorView::instancePropertyChanged(const QList > &propertyList) { QList changedItems; + bool needEffectUpdate = false; for (auto &nodePropertyPair : propertyList) { const QmlItemNode qmlItemNode(nodePropertyPair.first); const PropertyName propertyName = nodePropertyPair.second; @@ -873,10 +874,14 @@ void FormEditorView::instancePropertyChanged(const QListsynchronizeOtherProperty(item, propertyName); changedItems.append(item); } + } else if (propertyName == "visible" && qmlItemNode.isEffectItem()) { + needEffectUpdate = true; } } } m_currentTool->formEditorItemsChanged(changedItems); + if (needEffectUpdate) + updateHasEffects(); } bool FormEditorView::isMoveToolAvailable() const @@ -1011,7 +1016,7 @@ void FormEditorView::updateHasEffects() FormEditorItem *item = m_scene->itemForQmlItemNode(qmlNode); if (item) item->setHasEffect(false); - if (qmlNode.isEffectItem()) { + if (qmlNode.isEffectItem() && qmlNode.instanceIsVisible()) { FormEditorItem *parentItem = m_scene->itemForQmlItemNode(qmlNode.modelParentItem()); if (parentItem) parentItem->setHasEffect(true); diff --git a/src/plugins/qmldesigner/designercore/include/qmlitemnode.h b/src/plugins/qmldesigner/designercore/include/qmlitemnode.h index 5948ec7ab17..dde5515a5a0 100644 --- a/src/plugins/qmldesigner/designercore/include/qmlitemnode.h +++ b/src/plugins/qmldesigner/designercore/include/qmlitemnode.h @@ -111,6 +111,7 @@ public: QSizeF instanceSize() const; int instancePenWidth() const; bool instanceIsRenderPixmapNull() const; + bool instanceIsVisible() const; QPixmap instanceRenderPixmap() const; QPixmap instanceBlurredRenderPixmap() const; diff --git a/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp b/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp index e524e91c70f..59295e6ae76 100644 --- a/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp +++ b/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp @@ -480,6 +480,11 @@ bool QmlItemNode::instanceIsRenderPixmapNull() const return nodeInstance().renderPixmap().isNull(); } +bool QmlItemNode::instanceIsVisible() const +{ + return nodeInstance().property("visible").toBool(); +} + QPixmap QmlItemNode::instanceRenderPixmap() const { return nodeInstance().renderPixmap(); diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5rendernodeinstanceserver.cpp b/src/tools/qml2puppet/qml2puppet/instances/qt5rendernodeinstanceserver.cpp index 3a7882871c5..f9191950fd5 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/qt5rendernodeinstanceserver.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/qt5rendernodeinstanceserver.cpp @@ -36,6 +36,8 @@ #include "dummycontextobject.h" +#include + #include namespace QmlDesigner { @@ -217,4 +219,37 @@ void QmlDesigner::Qt5RenderNodeInstanceServer::removeSharedMemory(const QmlDesig ImageContainer::removeSharedMemorys(command.keyNumbers()); } +void Qt5RenderNodeInstanceServer::changePropertyValues(const ChangeValuesCommand &command) +{ + Qt5NodeInstanceServer::changePropertyValues(command); + + const QVector values = command.valueChanges(); + for (const PropertyValueContainer &container : values) { + // In case an effect item visibility changed to false, make sure all children are rendered + // again as they might not have valid pixmaps yet + if (container.name() == "visible" && !container.value().toBool() + && hasInstanceForId(container.instanceId())) { + ServerNodeInstance instance = instanceForId(container.instanceId()); + if (instance.isSubclassOf("QtQuick/PropertyChanges")) { + QObject *targetObject = Internal::QmlPrivateGate::PropertyChanges::targetObject( + instance.internalInstance()->object()); + if (hasInstanceForObject(targetObject)) + instance = instanceForObject(targetObject); + } + + if (instance.hasParent() && instance.propertyNames().contains("_isEffectItem")) + makeDirtyRecursive(instance.parent()); + } + } +} + +void Qt5RenderNodeInstanceServer::makeDirtyRecursive(const ServerNodeInstance &instance) +{ + const QList children = instance.childItems(); + for (const auto &child : children) { + m_dirtyInstanceSet.insert(child); + makeDirtyRecursive(child); + } +} + } // namespace QmlDesigner diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5rendernodeinstanceserver.h b/src/tools/qml2puppet/qml2puppet/instances/qt5rendernodeinstanceserver.h index 53cb6fffb1f..738aa47b185 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/qt5rendernodeinstanceserver.h +++ b/src/tools/qml2puppet/qml2puppet/instances/qt5rendernodeinstanceserver.h @@ -17,6 +17,7 @@ public: void clearScene(const ClearSceneCommand &command) override; void completeComponent(const CompleteComponentCommand &command) override; void removeSharedMemory(const RemoveSharedMemoryCommand &command) override; + void changePropertyValues(const ChangeValuesCommand &command) override; protected: void collectItemChangesAndSendChangeCommands() override; @@ -24,6 +25,8 @@ protected: void resizeCanvasToRootItem() override; private: + void makeDirtyRecursive(const ServerNodeInstance &instance); + QSet m_dirtyInstanceSet; }; From 3e709802790426130e541d54a27aa8bff007f4e4 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Tue, 6 Feb 2024 17:20:33 +0200 Subject: [PATCH 022/176] EffectComposer: Commit float and int fields on focus out Fixes: QDS-11919 Change-Id: Id1750142d9a45b723f615d6a7da3989285f4b7aa Reviewed-by: Mahmoud Badri Reviewed-by: Qt CI Patch Build Bot --- .../qmldesigner/effectComposerQmlSources/ValueFloat.qml | 2 +- .../qmldesigner/effectComposerQmlSources/ValueInt.qml | 2 +- .../qmldesigner/effectComposerQmlSources/ValueVec2.qml | 4 ++-- .../qmldesigner/effectComposerQmlSources/ValueVec3.qml | 6 +++--- .../qmldesigner/effectComposerQmlSources/ValueVec4.qml | 8 ++++---- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueFloat.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueFloat.qml index f33d33d9c9d..7348d6668be 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueFloat.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueFloat.qml @@ -22,7 +22,7 @@ Row { value: uniformValue stepSize: .01 decimals: 2 - onValueModified: uniformValue = value + onValueChanged: uniformValue = value } StudioControls.Slider { diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueInt.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueInt.qml index 5fec6b1de18..89f571c8cb7 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueInt.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueInt.qml @@ -22,7 +22,7 @@ Row { value: uniformValue stepSize: 1 decimals: 0 - onValueModified: uniformValue = Math.round(value) + onValueChanged: uniformValue = Math.round(value) } StudioControls.Slider { diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueVec2.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueVec2.qml index cd6a4330247..b703d5f184a 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueVec2.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueVec2.qml @@ -26,7 +26,7 @@ RowLayout { value: uniformValue.x stepSize: .01 decimals: 2 - onValueModified: uniformValue.x = value + onValueChanged: uniformValue.x = value } Item { // spacer @@ -62,7 +62,7 @@ RowLayout { value: uniformValue.y stepSize: .01 decimals: 2 - onValueModified: uniformValue.y = value + onValueChanged: uniformValue.y = value } Item { // spacer diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueVec3.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueVec3.qml index e7ab2ed965e..d59b63a514f 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueVec3.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueVec3.qml @@ -26,7 +26,7 @@ RowLayout { value: uniformValue.x stepSize: .01 decimals: 2 - onValueModified: uniformValue.x = value + onValueChanged: uniformValue.x = value } Item { // spacer @@ -62,7 +62,7 @@ RowLayout { value: uniformValue.y stepSize: .01 decimals: 2 - onValueModified: uniformValue.y = value + onValueChanged: uniformValue.y = value } Item { // spacer @@ -98,7 +98,7 @@ RowLayout { value: uniformValue.z stepSize: .01 decimals: 2 - onValueModified: uniformValue.z = value + onValueChanged: uniformValue.z = value } Item { // spacer diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueVec4.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueVec4.qml index 404ccad5936..7e930abf815 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueVec4.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueVec4.qml @@ -26,7 +26,7 @@ RowLayout { value: uniformValue.x stepSize: .01 decimals: 2 - onValueModified: uniformValue.x = value + onValueChanged: uniformValue.x = value } Item { // spacer @@ -62,7 +62,7 @@ RowLayout { value: uniformValue.y stepSize: .01 decimals: 2 - onValueModified: uniformValue.y = value + onValueChanged: uniformValue.y = value } Item { // spacer @@ -98,7 +98,7 @@ RowLayout { value: uniformValue.z stepSize: .01 decimals: 2 - onValueModified: uniformValue.z = value + onValueChanged: uniformValue.z = value } Item { // spacer @@ -134,7 +134,7 @@ RowLayout { value: uniformValue.w stepSize: .01 decimals: 2 - onValueModified: uniformValue.w = value + onValueChanged: uniformValue.w = value } Item { // spacer From e43b0bcc1fb9c25b3f8a2052bae3c93bb19c8fc8 Mon Sep 17 00:00:00 2001 From: Shrief Gabr Date: Mon, 5 Feb 2024 13:56:13 +0200 Subject: [PATCH 023/176] QmlDesigner: Fix unchangeable data in cells MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Changes to the cell data are saved if Return or Enter keys are pressed. Task-number: QDS-11894 Change-Id: Ia3a1ff8f6c9275906736fa1e0638b81c92bea910 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Henning Gründl --- .../CollectionDetailsEditDelegate.qml | 20 ++++++++++++++++++- .../imports/StudioControls/RealSpinBox.qml | 13 ++++++++++-- .../imports/StudioControls/SpinBox.qml | 12 +++++++++-- 3 files changed, 40 insertions(+), 5 deletions(-) diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsEditDelegate.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsEditDelegate.qml index 6a3146a8188..90b3021c960 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsEditDelegate.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsEditDelegate.qml @@ -91,6 +91,11 @@ Item { realStepSize: 1.0 decimals: 6 trailingZeroes: false + + onActiveFocusChanged: { + if (realField.activeFocus) + realField.contentItem.focus = true + } } } } @@ -112,6 +117,11 @@ Item { from: -2147483647 to: 2147483647 decimals: 0 + + onActiveFocusChanged: { + if (integerField.activeFocus) + integerField.contentItem.focus = true + } } } } @@ -149,7 +159,7 @@ Item { target: editorPopup.editor function onActiveFocusChanged() { - if (!editorPopup.activeFocus) + if (!editorPopup.editor.activeFocus) editorPopup.close() else if (edit) editorPopup.editor.editValue = edit @@ -163,6 +173,14 @@ Item { editorLoader.changesAccepted = false editorPopup.close() } + + function onReturnPressed() { + editorPopup.close() + } + + function onEnterPressed() { + editorPopup.close() + } } } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/RealSpinBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/RealSpinBox.qml index 37612af13f8..4f51d7aa1ff 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/RealSpinBox.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/RealSpinBox.qml @@ -90,6 +90,11 @@ T.SpinBox { value: 0 to: 99 + function checkAndClearFocus() { + if (!spinBoxIndicatorUp.activeFocus && !spinBoxIndicatorDown.activeFocus && !spinBoxInput.activeFocus) + control.focus = false + } + validator: DoubleValidator { id: doubleValidator locale: control.locale.name @@ -151,7 +156,7 @@ T.SpinBox { validator: doubleValidator function handleEditingFinished() { - control.focus = false + control.checkAndClearFocus() // Keep the dirty state before calling setValueFromInput(), // it will be set to false (cleared) internally @@ -165,7 +170,11 @@ T.SpinBox { control.compressedRealValueModified() } - onEditingFinished: spinBoxInput.handleEditingFinished() + onEditingFinished: { + spinBoxInput.focus = false + spinBoxInput.handleEditingFinished() + } + onTextEdited: control.dirty = true } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/SpinBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/SpinBox.qml index da2a7f1d5d7..ffde6e4ad16 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/SpinBox.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/SpinBox.qml @@ -69,6 +69,11 @@ T.SpinBox { editable: true validator: control.decimals ? doubleValidator : intValidator + function checkAndClearFocus() { + if (!spinBoxIndicatorUp.activeFocus && !spinBoxIndicatorDown.activeFocus && !spinBoxInput.activeFocus) + control.focus = false + } + DoubleValidator { id: doubleValidator locale: control.locale.name @@ -132,7 +137,7 @@ T.SpinBox { __parentControl: control function handleEditingFinished() { - control.focus = false + control.checkAndClearFocus() // Keep the dirty state before calling setValueFromInput(), // it will be set to false (cleared) internally @@ -146,7 +151,10 @@ T.SpinBox { control.compressedValueModified() } - onEditingFinished: spinBoxInput.handleEditingFinished() + onEditingFinished: { + spinBoxInput.focus = false + spinBoxInput.handleEditingFinished() + } } background: Rectangle { From 87dd0096b43fc8a8a413c872f82262ac1d391058 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Fri, 2 Feb 2024 18:40:04 +0100 Subject: [PATCH 024/176] QmlDesigner: Disable share button in open source version Task-number: QDS-11627 Change-Id: I3eb21fa84b6993aa783713ef5fa3beba1e3e030e Reviewed-by: Tim Jenssen Reviewed-by: Qt CI Patch Build Bot Reviewed-by: --- share/qtcreator/qmldesigner/toolbar/Main.qml | 4 ++++ .../components/toolbar/toolbarbackend.cpp | 10 ++++++++-- .../components/toolbar/toolbarbackend.h | 4 ++++ src/plugins/qmldesigner/dynamiclicensecheck.h | 19 +++++++++++++++++++ 4 files changed, 35 insertions(+), 2 deletions(-) diff --git a/share/qtcreator/qmldesigner/toolbar/Main.qml b/share/qtcreator/qmldesigner/toolbar/Main.qml index af838878323..294422df3ed 100644 --- a/share/qtcreator/qmldesigner/toolbar/Main.qml +++ b/share/qtcreator/qmldesigner/toolbar/Main.qml @@ -337,6 +337,8 @@ Rectangle { iconFont: StudioTheme.Constants.font buttonIcon: qsTr("Share") visible: !root.flyoutEnabled + enabled: backend.isSharingEnabled + tooltip: shareButton.enabled ? qsTr("Share your project online.") : qsTr("Sharing your project online is disabled in the Community Version.") onClicked: backend.shareApplicationOnline() } @@ -458,6 +460,8 @@ Rectangle { width: shareButton.width iconFont: StudioTheme.Constants.font buttonIcon: qsTr("Share") + enabled: backend.isSharingEnabled + tooltip: shareButton.enabled ? qsTr("Share your project online.") : qsTr("Sharing your project online is disabled in the Community Version.") onClicked: backend.shareApplicationOnline() } diff --git a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp index c3073d58e54..7ca17dfcdd8 100644 --- a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp +++ b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp @@ -7,11 +7,12 @@ #include #include #include -#include -#include +#include #include #include #include +#include +#include #include #include @@ -734,6 +735,11 @@ bool ToolBarBackend::projectOpened() const return ProjectExplorer::ProjectManager::instance()->startupProject(); } +bool ToolBarBackend::isSharingEnabled() +{ + return !QmlDesigner::checkOpenSourceLicense(); +} + 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 eb258f9ab75..5d0b0e712a4 100644 --- a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.h +++ b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.h @@ -96,6 +96,7 @@ class ToolBarBackend : public QObject Q_PROPERTY(bool isQt6 READ isQt6 NOTIFY isQt6Changed) Q_PROPERTY(bool isMCUs READ isMCUs NOTIFY isMCUsChanged) Q_PROPERTY(bool projectOpened READ projectOpened NOTIFY projectOpenedChanged) + Q_PROPERTY(bool isSharingEnabled READ isSharingEnabled NOTIFY isSharingEnabledChanged) public: ToolBarBackend(QObject *parent = nullptr); @@ -144,6 +145,8 @@ public: bool projectOpened() const; + bool isSharingEnabled(); + static void launchGlobalAnnotations(); signals: @@ -163,6 +166,7 @@ signals: void isQt6Changed(); void isMCUsChanged(); void projectOpenedChanged(); + void isSharingEnabledChanged(); private: void setupWorkspaces(); diff --git a/src/plugins/qmldesigner/dynamiclicensecheck.h b/src/plugins/qmldesigner/dynamiclicensecheck.h index a3ddf6c1927..95ffa8a7c44 100644 --- a/src/plugins/qmldesigner/dynamiclicensecheck.h +++ b/src/plugins/qmldesigner/dynamiclicensecheck.h @@ -133,4 +133,23 @@ inline bool checkEnterpriseLicense() return true; } +inline bool checkOpenSourceLicense() +{ + if (auto plugin = Internal::dsLicenseCheckerPlugin()) { + bool retVal = false; + bool success = QMetaObject::invokeMethod(plugin, + "checkCommunityLicense", + Qt::DirectConnection, + Q_RETURN_ARG(bool, retVal)); + + if (success) + return retVal; + } + + if (Internal::dsLicenseCheckerPluginExists()) + return false; + + return true; +} + } // namespace Utils From c491d652abf81970ca345958e954fc9e278c0a5c Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Mon, 5 Feb 2024 17:09:35 +0200 Subject: [PATCH 025/176] QmlDesigner: Prioritize hiding asset_imports content in UrlChooser MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the same file is available under asset_imports and outside it, prefer hiding the copy under asset_imports in UrlChooser when duplicate hiding is enabled. Fixes: QDS-11908 Change-Id: I06ac298e1ea45f6a94c66eb20cc4c0d4655103f4 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri Reviewed-by: Henning Gründl --- .../imports/HelperWidgets/UrlChooser.qml | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/UrlChooser.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/UrlChooser.qml index 6c0e334dec6..5d65bb09fc4 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/UrlChooser.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/UrlChooser.qml @@ -425,7 +425,7 @@ Row { // QtDS very slow. This will happen when selecting different items in the scene. comboBox.model = {} - let nameSet = new Set; + let nameMap = new Map; if (root.defaultItems !== undefined) { for (var i = 0; i < root.defaultItems.length; ++i) { @@ -437,22 +437,32 @@ Row { name: root.defaultItems[i], group: 0 }) - nameSet.add(root.defaultItems[i]) + nameMap.set(root.defaultItems[i], i) } } const myModel = fileModel.model for (var j = 0; j < myModel.length; ++j) { let item = myModel[j] - - if (!root.hideDuplicates || !nameSet.has(item.fileName)) { + if (root.hideDuplicates && nameMap.has(item.fileName)) { + // Prefer hiding imported asset files rather than other project files + let listIndex = nameMap.get(item.fileName) + if (comboBox.listModel.get(listIndex).absoluteFilePath.includes("/asset_imports/")) { + comboBox.listModel.set(listIndex, { + absoluteFilePath: item.absoluteFilePath, + relativeFilePath: item.relativeFilePath, + name: item.fileName, + group: 1 + }) + } + } else { comboBox.listModel.append({ absoluteFilePath: item.absoluteFilePath, relativeFilePath: item.relativeFilePath, name: item.fileName, group: 1 }) - nameSet.add(item.fileName) + nameMap.set(item.fileName, comboBox.listModel.count - 1) } } From ca6e6e4f3ec5cfb89cf7b58c3abfde31f73c5de2 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Thu, 8 Feb 2024 15:52:51 +0200 Subject: [PATCH 026/176] QmlDesigner: Fix validation on integer spinboxes Now spinboxes use IntValidator if decimals is set to zero. Fixes: QDS-11922 Change-Id: Ibe8c8a18294a6764a0cd2182d09c30cdf9fb27cc Reviewed-by: Mahmoud Badri --- .../imports/StudioControls/RealSpinBox.qml | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/RealSpinBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/RealSpinBox.qml index 4f51d7aa1ff..9072a2de8ae 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/RealSpinBox.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/RealSpinBox.qml @@ -95,7 +95,7 @@ T.SpinBox { control.focus = false } - validator: DoubleValidator { + DoubleValidator { id: doubleValidator locale: control.locale.name notation: DoubleValidator.StandardNotation @@ -104,6 +104,15 @@ T.SpinBox { top: Math.max(control.realFrom, control.realTo) } + IntValidator { + id: intValidator + locale: control.locale.name + bottom: Math.round(Math.min(control.realFrom, control.realTo)) + top: Math.round(Math.max(control.realFrom, control.realTo)) + } + + validator: control.decimals === 0 ? intValidator : doubleValidator + ActionIndicator { id: actionIndicator style: control.style @@ -153,7 +162,7 @@ T.SpinBox { id: spinBoxInput style: control.style __parentControl: control - validator: doubleValidator + validator: control.validator function handleEditingFinished() { control.checkAndClearFocus() From b1362ddda674f902df968e98ae0c2769cb050927 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Thu, 8 Feb 2024 15:34:12 +0200 Subject: [PATCH 027/176] QmlDesigner: Do not register Effects for item library Fixes: QDS-11925 Change-Id: I981853d1c02d99cfb391209a9a7e568bf52401cf Reviewed-by: Mahmoud Badri Reviewed-by: Qt CI Patch Build Bot --- .../components/componentcore/modelnodeoperations.cpp | 5 +++-- .../designercore/metainfo/subcomponentmanager.cpp | 5 ++++- src/plugins/qmldesigner/qmldesignerconstants.h | 1 + 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp index a616f3cfe45..a9a86579567 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp @@ -1654,7 +1654,8 @@ void openOldEffectMaker(const QString &filePath) Utils::FilePath projectPath = target->project()->projectDirectory(); QString effectName = QFileInfo(filePath).baseName(); - QString effectResDir = QLatin1String(Constants::DEFAULT_ASSET_IMPORT_FOLDER) + "/Effects/" + effectName; + QString effectResDir = QLatin1String(Constants::DEFAULT_EFFECTS_IMPORT_FOLDER) + + "/" + effectName; Utils::FilePath effectResPath = projectPath.pathAppended(effectResDir); if (!effectResPath.exists()) QDir().mkpath(effectResPath.toString()); @@ -1693,7 +1694,7 @@ void openOldEffectMaker(const QString &filePath) Utils::FilePath getEffectsImportDirectory() { - QString defaultDir = QLatin1String(Constants::DEFAULT_ASSET_IMPORT_FOLDER) + "/Effects"; + QString defaultDir = QLatin1String(Constants::DEFAULT_EFFECTS_IMPORT_FOLDER); Utils::FilePath projectPath = QmlDesignerPlugin::instance()->documentManager().currentProjectDirPath(); Utils::FilePath effectsPath = projectPath.pathAppended(defaultDir); diff --git a/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp b/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp index 936c23fa57a..403731d1c43 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp @@ -344,8 +344,11 @@ void SubComponentManager::unregisterQmlFile(const QFileInfo &fileInfo, const QSt void SubComponentManager::registerQmlFile(const QFileInfo &fileInfo, const QString &qualifier, bool addToLibrary) { - if (!addToLibrary || !model() || fileInfo.path().contains(QLatin1String(Constants::QUICK_3D_ASSETS_FOLDER))) + if (!addToLibrary || !model() + || fileInfo.path().contains(QLatin1String(Constants::QUICK_3D_ASSETS_FOLDER)) + || fileInfo.path().contains(QLatin1String(Constants::DEFAULT_EFFECTS_IMPORT_FOLDER))) { return; + } QString componentName = fileInfo.baseName(); const QString baseComponentName = componentName; diff --git a/src/plugins/qmldesigner/qmldesignerconstants.h b/src/plugins/qmldesigner/qmldesignerconstants.h index 81108dee410..cd5a8a2b238 100644 --- a/src/plugins/qmldesigner/qmldesignerconstants.h +++ b/src/plugins/qmldesigner/qmldesignerconstants.h @@ -78,6 +78,7 @@ const char QUICK_3D_ASSET_IMPORT_DATA_NAME[] = "_importdata.json"; const char QUICK_3D_ASSET_IMPORT_DATA_OPTIONS_KEY[] = "import_options"; const char QUICK_3D_ASSET_IMPORT_DATA_SOURCE_KEY[] = "source_scene"; const char DEFAULT_ASSET_IMPORT_FOLDER[] = "/asset_imports"; +const char DEFAULT_EFFECTS_IMPORT_FOLDER[] = "/asset_imports/Effects"; const char MATERIAL_LIB_ID[] = "__materialLibrary__"; const char MIME_TYPE_ITEM_LIBRARY_INFO[] = "application/vnd.qtdesignstudio.itemlibraryinfo"; From c6705be88b98928119d17affe5a3a89423e53120 Mon Sep 17 00:00:00 2001 From: Johanna Vanhatapio Date: Wed, 7 Feb 2024 16:24:50 +0200 Subject: [PATCH 028/176] Doc: Describe the Effect Composer Fixes: QDS-10528 Change-Id: I46d7ab1977acb7f0af5c0aa790de7fbf2624a449 Reviewed-by: Mats Honkamaa Reviewed-by: Qt CI Patch Build Bot --- .../images/icons/save-effect-composer.png | Bin 0 -> 314 bytes .../images/icons/assign-effect-composer.png | Bin 0 -> 1547 bytes .../icons/dragmarks-effect-composer.png | Bin 0 -> 1421 bytes .../images/icons/save-as-effect-composer.png | Bin 0 -> 1712 bytes .../images/icons/save-effect-composer.png | Bin 0 -> 314 bytes .../icons/studio-clear-effect-stack.png | Bin 0 -> 1640 bytes .../images/studio-effect-composer-assets.webp | Bin 0 -> 149612 bytes .../images/studio-effect-composer.webp | Bin 0 -> 81872 bytes .../src/qtdesignstudio-toc.qdoc | 1 + .../src/views/qtquick-designer.qdoc | 5 + .../src/views/qtquick-effect-maker-view.qdoc | 89 ++++++++++++++++++ .../src/views/studio-qtinsight.qdoc | 2 +- .../src/views/studio-workspaces.qdoc | 2 +- 13 files changed, 97 insertions(+), 2 deletions(-) create mode 100644 doc/qtcreator/images/icons/save-effect-composer.png create mode 100644 doc/qtdesignstudio/images/icons/assign-effect-composer.png create mode 100644 doc/qtdesignstudio/images/icons/dragmarks-effect-composer.png create mode 100644 doc/qtdesignstudio/images/icons/save-as-effect-composer.png create mode 100644 doc/qtdesignstudio/images/icons/save-effect-composer.png create mode 100644 doc/qtdesignstudio/images/icons/studio-clear-effect-stack.png create mode 100644 doc/qtdesignstudio/images/studio-effect-composer-assets.webp create mode 100644 doc/qtdesignstudio/images/studio-effect-composer.webp create mode 100644 doc/qtdesignstudio/src/views/qtquick-effect-maker-view.qdoc diff --git a/doc/qtcreator/images/icons/save-effect-composer.png b/doc/qtcreator/images/icons/save-effect-composer.png new file mode 100644 index 0000000000000000000000000000000000000000..af81061b6c73d3b23563a13795655d613f2e442f GIT binary patch literal 314 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!s7I14-?iy0WWg+Z8+Vb&Z8 z1_lQ95>H=O_7{wDg2GZ(Jl=m87#MDPx;Tbd^uE2cQP0IuqU~Y&^$;mRr=|%47gvc2 zl^+n@F|}A*WVOp`HBmGxLu<(qy!XCD7-&Y!ibR`qS{iJC9w z_Hoiqvt=8*-+mCfwy|+`0k_4Wv}fNA$tLJr6M9$B_ngE2;l2l1GD%$gH;#vOJ@2jO z^lv%6a$}RX&Lb}Cm+cR9!Z!QQI2iqA{l>Pul@G+yUr7AxylWv{&=|M!f$6R1hC=UT z_v)B;hGZflaV@Adn` Q3=9kmp00i_>zopr0J@5Qj{pDw literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/images/icons/assign-effect-composer.png b/doc/qtdesignstudio/images/icons/assign-effect-composer.png new file mode 100644 index 0000000000000000000000000000000000000000..0468ab066205177c87121c2bda4bd2f8cb6d9059 GIT binary patch literal 1547 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 R85kHCJYD@<);T3K0RX9HfQbMA literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/images/icons/studio-clear-effect-stack.png b/doc/qtdesignstudio/images/icons/studio-clear-effect-stack.png new file mode 100644 index 0000000000000000000000000000000000000000..503d43037b511211e715dc39ad9ad9bbb7260321 GIT binary patch literal 1640 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!s7I14-?iy0WWg+Z8+Vb&Z8 z1_qXQnIRD+5xzcF$@#f@i7EL>sd^Q;1q>iyV_#8_n4FzjqL7rDo|$K>^nUk#C56ls zTcvPQUjyF)=hTc$kE){7;3~h6MhI|Z8xtBTx$+|-gpg^Jvqyke^gTP3i$ zR(Zu%AYpwa1+bEmY+EHqkcA2nz5xo(`9-M;mU@P|$p!{S3g(u2X~w1|scFeN3PuJ- z#`=ba`UaM|hDKH<23AJq3Q(YAr(jc*l4cd;;s&*>C?(BSDWjyMz)D}gyu4hm+*mKa zC|%#s($Z4jz)0W7NVg~@O}Dr*uOzWTH?LS3W`avc7m0xRdD)WnkfqLBRj99T>Rz?`gLWU6PPpb?&#my%yztcj!{)g`ec)mF*K zz{o_`z);uNAjHtn%EZjd&{W&N$jZO~qSQA(B{QuOn^IE~D-#10rKu%}DTyVC1Qn+w zTcza}*lEl2^R8JRM zrHb4Fz0AxME0ZKc<77i~LtRS~0|VW}B$HI#L^Gol-82Iu%cK;8Bx3_Zh%bD7tz7bx zOF=OU)1N}3e*dD(^vt}(9J0*;WjIihu=2<(F3B&dgyx9g)IumLCsP5O3ak>7L8;qT zDH#;4sVPcO;Y_ka0Ag%;Mrt13=!PgO&QB{TPb^AxOi#@#A())>?G$YELHQ8k4}EZ| zNAewsD$C z;X}5`7o!hYEo=U0CT1+S+M@elU%iXW;k+l0Z=7bxQk1Y+^pD+v*Lq_ABaPxji?#<( z`$ea1l+vl%*m~1V+{-XRPhcZE|BQoaSEg;aw2FIb!jGA|FW)@VI!!KUG51Wqxr|kJ zP6eNf*}*9*bZ0UD^0f?aj_3bzioPyUHXV-m~mbbF7v^#|^LzT+xz@js^m z=k}@fuP5DMJn}^T$RsoKgF&ahI>|3l{jT_3k-z(K+!Tgf@90}x1>J9zZ>FT=EO<7N z#n7f;{@=MQta8p;XHFqT1d0s)-r%wuCtbmRwR}V3c^dlw-%{{#tvVUw(^@Zsa^*pjcm4(<+rbv21dWVTi{E z|6hN-rf#+OYvD@&b97=i^NqFYx9wl3`t9PYKDaz){gagZqG@GHr?=*A&z<&~!RY@+ zNpbOaq3>E8=Ew{78yPcp_NA=e7G!zjbNRgAXOI2-pI=z9Y15{nO@;0110oRr; zZ?ZQjOE`YwW9{sKN*?C+NeuTrtm~(E3R-J=E;IT2d+{aneVzhBOH7vwFK6L&H@kF( z(ZT5XJkFQ=lUqbQ6IXnB+Z{h?j(+0QPW>yh_A@R}vDp4@Mi;-2LiwXjOaj3SDc3o* zwktfD!YMSV`smA#O%y3-1YE5a8Y1Yq$ ztQ9Aov`w34xIA&`+|2V^-tBmQ_OC^9F=txwJvnZpRIQ*OFOd~K5v6<@5i1R?U)|qm zeA+#`pZoYYP!dFQF9|5A&4 zHq~$Qe_XjXLR0M7^m_SIKfgt8>M=UZ_~Ff?qDJWgDdWU9C)ldb%Wn_*K07(?mqvT* z!*^97^F`ZNzcJdlL`8Gbqs#g#Mcf+a`){vxvGmr~wLA5=E^%tuqd?u~*MstpA6j;E z+wJRb#cx||Wt{Rd@XT>T`8M_1Tk<6*L_|D1Pf2ZTS$woRcG1~uy{^_LXM7P_yVY;n zfp5`swry_bS}YfpasGwRgKJuwWxXd&QFwbt;F5myhMVeVpKT~$*gbXP&HwInYi~2@ED^h2e!J!;)0vx3 z=7)K|UYIyjGkn>Upr6VMw*}8OSiQUT?ahRwkH5-}ui#^-md;o!yEa~W>9%WGt8K5W zu6(BIZ2DPEIqooW|$GTOd9Mqwtyfg>9NWn3nS zSZuB?e|_JW<%dQ`2d9Jf_Gv0P`^@!wtxgDi+OE5Jx}cNL@+#iCQ+v-QN84->*d{Ph zg{3Oz@pOg!FW&x0PLi|hBgI}&X*EC^A#i=OU%X44OeAoQd`gXw8*qJNmElEFceE);HCH_IC zFW>P^S~J~d)vS3c##7Jqd{C_3{7cYOcG4=zRm*O833)JmXm==YfA#Ld-J7|&#lMbC zPzkZRqVh(Sa|!c)wn;5ptGB%FRjp6+_^z_@mD~nhPhGy8+4_@bol+6zQ=RbR=UFL+ z!k5AS!d7o>QsMjiRzF8t;Kl>_ZKi>@*H}z4xtabhC?PFOEZp?pnKNh3?09XwJJRTI zQP`fOHc?|^@it_%BZ&z-$(TkfmcGduT`7W{m+cth@Mdlw_G zCo@W)yy@7Id%JJS+2@t_q{aKTO?xggcXQ6|Rk@y%en}Lst36liAIf`2MQP^DnWuT~ zPk8!g%M=|W)zh<^``Czno?G;ESX~ZS+7Zahs)uAKW=UFElj?> zt;y(ULYvX<1A1v~=gt(E%y=o@^WWgyaYf_L8D|1`^wf;K#coa#Vp`|zbz5_}`|5(S zt{#r?ExEy0-JVR{w&Nt;MjaYDZJb*4#U5n>Gl~6VO^2zmJt6Nags=Z4E7l-pzYA zbK;ilKN5WRf7}0_G4oM%nPYjDSLdGn!h=lMI^{A5bZj%VFSx$VccO~trQdb)J0>*- zSGasMIR8%WpxnQIX=y9Yo#N>)FbWWu_+HkIt=#{X87MF={by!S669CX%6c~MbHz5B zY`x;Ss}~-darSD6>x`Kkf{b<0{q0H~(7HIBx$oK1E{0&i# zq`E)%c7J~(nx)`rwCV8ev}0$leRG?n;`#nJqrjpgXXd!1l%;(wu-p9h=9xdwYBqVK zxmE1gJyTPVspfw5F<;%HX$q%KZp*#ir{bCYzWDxcmWB?EjSrTHT;x)ByHo4Zy-8}# ztX!@r?@L_oj`)PKFg^JH|DEmaW);s>xwp5aip^a6ol#gUZ&&|JZHMWrjy~O${&=yF z!T$0eYj$3nWw7A;y{I;Jom*wIylyyG9-QdSabxcM_>cDI_dR=myz;F`VB%H&29Y_! zv+p<WRSTVvzmCASL(F$UO7rhAES3M1QEC`Ezjlm5Gw>YTfG+*Wk@ zu|8H%+92U}H2d-E{K&@H0X`0;A2KS>bkDi4;ajF~&m+0^iL*6|Oj`f1Wjhl7#e$`ddvnjf(pr{7b^>Pug_s;f7+7>)?~hW7I;?nNade#m zE7!53HHW4oymR}$b?$-lDr+BxRp>kCHa=Tu@%Ztsh{b<*%KtB5{B<0H zKGR!mCoy0Ac%)zc#Al0+-MCS~yuvuS_Ck8n-es)|!+1Y62HB>6zn@lJ!56E&_IKv$ zTA>3>a%+@aVm33FSzT|exL&b(63?fr%_-~ zrfI4$ZBU!=Q8V7jlR2S?k@Mt8XT$lpJ4P-Zk7Cf`{U)a3(qfAdyx7u`sAY- ze!Z+-$2_#xCe$CFIeF*zHLe#Pefx4!{a$IWw$Z$v3C|Wyl59whtGdBy>#ipioH*s_ zx{7&r*KN0?mIsIaJu;<2^IZz3Mi4uR1poF!;vDMd9LBY9wQ!Y zzs2G^7W-*<>A!Kt=I4+NFKk+HLML>s6G!9#22}@c-B6 z_L-vfs@ea<-M07h{-|G_{^_m%w+-K!zh&FkzUr)B5dHVB-rrrCYfJvd&HvQy`fd5y zfAaG~>&vzNd_A_c_~-Ust?hEh+?hQjefQm7ao*soP30BOuUEq!@6@e(U95QNWa5=c z9O`q_TNbbBFPZTCWwN?#%H~g>d;E-TY+P#Inv~95Z}{=GL6t&z-zCvgeIIpBg)sMS zerspvJ4q?Y-?Z$`%-z{R&#tWFz53;2hp+Exzp&QBJ7-z1^_l+s)&Bcmf6jV$>u#R$ z+K;!QcCKssE`GT*sbKBFZT(a7E&s{9i$A`^a`)a#KVGi-SY3Vp|LSQ;Qc+p+9_vNs z{Pa=^P_+Jcwfysc+k$`M*}MO1SN=VoR$Bil`EUQ-WA%A%zkf!2Ob_#mPg_>MV#Cip ztA1P-y`JwD{+;cQLD66KKfl(yEU#Y>{r$K6y8Uwd{xB=Qn*Z{lRlJ$*&CFlSv-9^q zyV+VPblv7p;@-b&=gKZ+a_m`h=<4Ca9dD&7=Na3zZ}w{LuiO=Tr}p~qOH-?7?b>}_ z`E_j2efQ(fUR^YHpL#0u!r~8!ZmJ^F3|B4*eP6h%bj>ZBa=z@K->+hgDlFQiKh;OE z9{v4oR;IdFe7RKjs?7^+gzf#`{FRE@=X%;cX72qqxz*nvde^IMPPFy+{r|J=;qoW5 zt-fz^3zfG$EvjoN_`JAUS$COY;epLQv-cL>b{BGS-@rfR|M%sWcHm$E({9je~Z>MI>9NquF7Jhxz6+c(+pVQwjt1qU`3fTLh+xVmY)3g6?%y?Eh zp+331%JtLzLf@Z@qo%aCDzqB$ns1L}e}5=Uv31_+SDEUa?>k<-ahUj6H2b@jrReb_ zyLnU9YrLK;S-!Y+DSMx}cI7?Rv$d=CtXgnPXVIiI@32N`zob$=?^k*1 zgkzSF=I@x;tJ$w!N?mk_)N}pjtmT^?xwUUsnOdgCvN<6um1iE7PUpS4xaR9>qhEg} zPiy`t!#6c%W&g^vDvMX2{h(M?boRoJum|V=K5FN_z+U%u`nM9J#rYWCS z2dsWHzl*>A{F6_cIqEA^{`4xJzF)Sy_Ux;^wdt?=WU5)Or=6*(lGwkmIp1rd^-H@Q zlmD!Xb=TIEYLUv_wr1ii2aCWx%grw*baPKlx|Swqx=QE48U6JumVM2O+x<}If7oK* zpI2X&2Y!#STsrI7guT1?=2kvSxy-C$+x6^&x1Y?sWNx9~R@P}5_hx5@EKtc{J&+2x0~e|@-K^-ppBAKkrO;*m>rR>+*&E0lBS%tKQ_ z^GPi0_*ax4ojd2=kwfNhJC20T+V^Ai{J(AQuYY;C{GZqVr~9{T3XScWyyE!;YqOAe zzbYH`ZcEdqGI^a?cvxGY)hs54(zs7Cso)zB1ULv!0U8UA;O)Yu%$CgHWMUULw zV#{8+^*?(B{ydazDd8Xdq?onJWYWxZT z2~R%mJh*m~>eI(*Ijw43J7alXB&53l!U+vKm~!1_Aa-3Ox=@S57o` z1)q*;x}K0Rskb+ug-7AS$64op@mO^#%_zK;oBjLKQSpCw`t83*zUL6k?ajUYq-R3Y z#0mToC9Q!BI~)$E_<1%6Tv#=sMMckoC2g4kL&F5N4n+<{wv`(n(lotkhZz?$3Vg^^o1k<`ok5$0q2-g{qzR7}lsyx1}Rvqln^0sl#F)eCwG+f~@O*r2hL_P*GET;ULo& z>$=C{T-(@w&#q^9yrg;GzxmsuTUPDR`2SLPU%@+XN$Kg=x{MunH>muI+h~7x;`d8l z4Ojl1GyPxpC0+Z%&yM$JS~>anUtfH9@7dlXRw@dT7oIK&dMWbc`k%b*?2)~TA3wIf zef$2Wy}M7QEm*|ka@tAUGpcs|A^WLfb645Fyl~%bhC+#4+1oP{*B}4)UH_i1d}#8u z#wlDO;mYs+|7tobwy?R>ZcdTm?9%6Fk~p?FG2N9o-td3o-v8U?9CTUwf1%!=#V0D4 z^Zsu*^FO}FivQTTn$|trzE9s*`MvC^M^EXA_(ek97v}%?$uGXZ{#)$z8*=?xb3^QA z`|$0R=4~!1=CbXR>Z@i$z1&%%c@@7AwQW;n3@??a`Bfjs8RC#-zxw%u%{jNIg33(hM4 z&q}=$^soB;?{*Qzo?qAc^&{+l-MOshskwhkPE6lSqaSNkB5K*30?y6-S6}+(f9Xa0 zIUG|OOc#0g)SN!9zrHVO`N_nir)H zb;nCPyK{y7J-_kU`hU+^zKU9e{5`+W`@W)ysA5Cxo2iffrZRCIxz4V#R#i@*WMdMi z!rMbjBn=nl-~YqR8qgrXBA~>gXSVy+~Uvt6C8 zleDsQQ3A&&0Y&!-tBUeZPG5a`g9*dS#UG_Ap0Bw*ok4-&(Tr0?@~3sfj2~OG?r1x4 z%IDp|7G;OZvQx~VoD;p>qjJ4{!nj2lPAag=6~B@E_A&9NT0(24@BxG6^VM&Bo9Q^o zs646YmR92QS3E)Q{*{)T4zQYdQt{{GU70`L?mRqcShDQzX^a}f*I`De>qQcq+$hLlcfjqN@cS@`PkqNtZ5J{yd#b8UUT^zWY9vyt`n zrPlwu|JL7Feg6B*M}M#H-c$BuqMEdQ#LBnbX3j!i-n}h;wV^(C-TjmG4Ev4${(D;R z{p{WUewCYnjX}lpx$pVtKS%R69ewr1Dd=BA6?@Ibs+^$yy!Ce< zut-X#?Rp`onCW$B#tFVdPNoy4YPm!dWV0}_^f*^?S#aejtk|O4^-7p~Qc*gy1Gh|P zkn8MXVeeDRSsA=EgdR6*hqowPjM%{Y!)u#*F4kM_z*UNX)nb{+9l&p2k5-h2Iz!&&Qn5n6WK4kwnjW%5mE&&;)1w{mvB zsQI=|O~suNu0BrhtgpW9{r&b;y#IRRqdfaQ9~5_Lm>Cu5a&yLoS4@W%y=Lw-VY$U3 z|HOA*hWmQmIesBZj7&qNX-MJC|A_t_NxGe3h{eGkW z!^atN%5HmZ<#?JL`Fea`u;{wO+LJG-3AD;BU}RyD2+$N|IT&%#=*mt$h0e)KM7MV; zaxrk-7g=7z-z3IxOs&w0uc>9j(`zl7E}jQf+JNmajHboKQGs|)R2-mX9YEs`tF?Ao`>rDqF@ z%0r7~1RO*!*i^0j&-uFU@2{tJz3;cYxwPnf&l|b-=QotE{&ATz`#{aU(%U8XC%=1k z>VCvWnOl3~!ggyp)Oa~4FtAMe)HsEK$w7pXlZl~G!EQrj?fTd6J_z0l2~u@U*ZS7h zwcvub{qkxJ-JA8XD^ z?BL&dwPbb8{2BM!rawK~9{fCBfJO1`rO408CLJNQ^S`gzWj6chuDl&zR!H03UiKu| z*5b#uW%v7^a>>rA-^4hP>F89ylUDv;=B-=TeqHK8d|#Nxop|$g??vBdf3Jv@wF|qa zyteV;GST|D)cEdGm!@Ctde!b8=P*0lam}ywKdx(E)o1THabiT{DY5PCjemtZ0%O{y0Rx$1*zK;<)0{d*&Ap6@MHI4 zORfsX_4hx2J^uBZ$`svO(^OP67v0(vmf-R((fdZ?w`HuEdf$5!lpoEy)K<46{ou0= zQufRqb8Mf@kpIAQXQkzq*9;6^2J>W2PoBM_ZpQ0XlLKupZCKj7He$-7Ak9TKE)I7l zKKrur*lm%!-!6Lpin-RjyEfzdw9cu5r+8l#o||E&&407>)0ONqyFbUpoW0(g#@M)s zLzF>PV)mvC@r{3_?w+4eCKUFh#7RN(!&Uy}NzXTJ>ug-2ZoB5e(=g9?U zroT4-{Qmu~#Xq(`zOcY2f$Z zTOB?r1qK$5SxT2~aUWz)BV@5oSaQ8(WKq$4uHKxRQ z7|zdpe?Q0Wzr5W&$>R~}-!^_-mUTgLGP}#0Q@%64zjSH=@Pu&BbUZhAQvF0W`5mQzw~^Z^}APlYy9}ky-VMEKYR0SUaq;V zz5J~A^Qzk~sYIJC`dxR)_H3fBf7PRnuU43^im81o{5{w2>cT$V1g?!$yvw6^^S5UI z5zo8N7xpP+E(1&F)?SBz z?p^6Vv%>6liOTJXnXHxmYL>ZHJ^PxgduCs_W^i?Fxb6xG_g+KZ^j&OR>Hb^!*KX1; zwF{c)cvw~E)-hbQS~S#u*Fbmam#MdHKAQ zOXcCByS$v8q<{1#~G!nlg_K&9~gCr@XUtlj#p*9X2(8@Mc7W4?KU_)@$8>{yZUP4%$LfW?|-`9SpM>Dw%M2GW<2}6E6#YW#rk{K z`#nMMC(sAFVPWxXzy4#h% zAys$BuIsly?_T@#und;8?pk^>Av7iO8QxNoa(w#Rzq z@x|BcKg&!FyH~e=-{jAGBGL296b-dNY)(%f2jKY9M@(Q(N@t##(*{@t0-?W5g@I#B7iM`>@XU z-qblJ#<9tD2j8#GvXEDmaMXTx>zT>v%wqe(SBdxM+P|Kez$voo6xEc_jBl46}Nn|0aK-+qqtso*m2>-ROk zytK@dUB8N3c8wRWua4Tx*So~SxBlM#HTiot4}ZY)-<%FwmbVQ59`8A093y(~e)Xin zg9&}6!DV}YO_IBJY+9_w#-@b~9*1hae#?#Wf44qc`{O4g9oO7D?dx{GkFyr{HQ|uH z!eXAjGN5+u*P}YleCG;lgk~+-X|{sRtBcLMrp$kS$JbEj*kJKcQEAgD>mM;!vq#Q~ zS|+z?ftvSP?`H~EUot54lqvhJEQ;JSrJ>Qu!|+@A=CrI_tE>hFL(cVoN?e?Sn|`lY z)_THR=;ti+7r)*=Tl=G9&daL@nzK*Yc<2B9DPd&JFo&J@eyPH}ybRg`bWV(40)wBhQU?X&Ma_V!F%c6z^Prd`vg5u}(9$j;(HldSCathPSLk=QQt)|97TaUi!zbD)uy`*g0Jds5a6Zjh! zSC_D8GBlJpl_d&%u;VO!c{jD$b+NnDCEG>mS*B~YNi34mM74Jv9daNo@Ep`{P&l^$$-6IR&`5nX8-#C>hW`rG?#NKYCW*|JzM*)W3ZWg_3h=R zo{9>YlN4QUxL>$vSXH?y^o;wO-32ahqN=s7z3z@LGxC^SU7@zUW5I5}tr@?~xP4Rk zCK}q`4mjY_^=!t3;4^`mR-W2^YXgdfO21|CR?c~STI5hGqt0=!yZOuFCFOLNo@3{@ zbYf*y_`WqS9r7Q?GYbW9D6$F&oqx1u-?KL_?(T`!H_K1UV$J$;Azd}K>g&pLi*)mC zrW@vj>BY-LA4ra5|Q&cf>*Cl7mnTy#DxU3sN!`f1tvxwq}|Bi~(~skZz6jc?oB zEAHgWK3n=v=yKfl^rJ^_Eee>ib-T^}lD9we_?BC}ji_qOu;^D87_dNV~;Oj5fipT5HKYTf}d3(O{*AutTpN^LMGimL|*Yi96 zOliDvysSestu#@LrJxJ*Pagxw*KkGHMa;KZms>(jcR#~29fA(P*yW7VWrp&Ukyidm$#hzGWUBHd0f}+FyxyFv`r~XWhA|L2Glcr-Dyz^}W2R z%O*Mn9gGbMnO~nCZM|DrvH0@xn^x;y&tKR2TIT=1H=B&6DclwN=%>Q&Uk3egC>1l``dmqGk@m%BIeOujzh>}EuGkTt zU;Q>rVeX!(fpL3pUB8w4cJGvld_ql82mjBpubpGK z?DwB3M})f0IBz|BR)~@Rl0)O6CmkYciW)~4mYx(`Ezsei$TUSynoYsQP*bUOrNNS9 zk#LR$kxsLOuPHDs`fSk7^ulF>hYN?0K<;nP&c~@mo2qADEVipUr10`dR#Z?)=AVuv zvF?g>-?uQbynh_GWctQ6SKZ^UuH30rUussAVxE8ZhV0r#vttc{(-fB2@Dhh(I70y!gTpL&6ZcJ0 z-@i@_{d$#+U4)@gNkoO=;L-^!4$3T=ejHpET^JQO7HkO6Xz}HANC;wK$PjT1XJBGE zFjax2)>W8MZDxSPMTtcLMykyXJB^KanOd9*iiICe-F$9as|SbB^%WfJydJMQE0ecH zZKlv%`{rm*d#&|g%htskIb^z9qSO~(v)%f|;K9Nvv;27#=WDI*6Hu72 z_O#XAZ!JuGH(hfaj1(BAHSIrIe^b6MX~Xf2mgm1RugUBy{k*Sa)ApI>N^5skHbs47 zo~Y%iT42}Ou*E6ZK_O$|n#)DG4F)!B%l93b-f)Y7BWaS;OpaN44i_R_uX_o8o6*i0 z@nMMp#|D>e4$obUMPBgP9D1Vdf8OxQzmc1}3(|Flx$&t_WEp@_MJKGSi== zLbcf;g7%8+BHK>>@$pcVmE@Z8=E;FOWk{C3KtU?iAUW0migzl6( z;ZaBbuRJMptgA8Z<}6jlkbU_N8$2ge{r%Cvz|O-k!J$E$L+Rq882zU<0S?Q&*A>kD z&>~iQG-g@bM(Js(j?Zi7O>0tcwU#ZIz{0^`u%MacfkTFcg5W8mg3L3%BF>iS$9xwQ z>~^1e^@uiW|I7`-o5U4Zlo%Kq&;2^l$R?q{pfI7Kd7%Qk3~PD=OYH%N05-nHY5I?} zfAGjJa*B}ncyLOa%=@1A55gI9wx2Kldu{%$Tie(Ig(hUIEq9wViGih|p@D@Z_Ed&M zaPpgm6lVF7XW`0xK|5O6xEdIB3Ya7oNOLqA++anxN^iB$FgJG3~ltR~UA(mWtiNQ-jzi01PfcfJY1lEvz52-a1y7Xs_4CJYsyN1+SRSLeP>iAF%+g!I zmtzAmLmdh-e{^stUh4d<$#`f1cL76#Lz96+0oxG=wjEC_r-=V#I#<#8di#t`5h~5? zLd?eJ0;YMhgl^c|%KlyQ`q|aT%+GRjvUhl=`qdr|xjt3!#XC zQ*2+}5;lM9dOM)xjdb`YtM5C%l(I(E#MxeHSZloOxQy1NnB;SE`!x@*w=Dj8XBso3 z%MrzS9~!4bwJ!-!$ha%+bg@}Qm4Rb{)%%4Ji&z-uYcnu4Nj&h_&8RTH^W6E1$2LC8 z-f6dT+PT?cY#IzqAq~X|3@6w)19+1fnpq@1GMQH%=m<`mvmq|uAMGBycfwn5Z1(7h_RXWMJ@QEAF4W{F|u$w0pH@HWWJD z-L#pT-H7k=wYYA*amB`%d?3lAR6TwQYC<%qb`#)j$aF3V-@7&MpmD)x&BslR1CQlVn@ zzlLYO+`a#WJ0pV|O0FF$)O*1yVZqCy!T#5g^#$9m=D7;&Q#^fl9*o{IecJg?Hsuu) zgVV08;kE3UpY`KF^XH!%zXU(2p7`>sged2XC1y+pp2FU?gy+wzY(p|~xTkEA zop5V~2;Y;H8hnn=!Y%LqY~FO6cftwIqs^==9ZR?nXUUWK~p|Yr$)zD4xk8Q>e(s641(bl#`i5 zW#>(wiy5cXc_%Lku51gpNb}`TI4v@3UAx}%jn9_6Em`o&HZpJhZ2#x&AHP_IT)MMu zcMsd74d?cM3qGv#hHblEpWTe5@vrx3-(T&2Y@JZSY&U`BcCF{t^`1@H^GN%#&*`ro z``a2`E7f|MFLLwdWI5C`fqycC00WO@&$KWNV}(YhW=Rz{l?Qw+N-d5qjSh?s3~H}G z&T*4c+p%+N+>yGfn=&(FEIr!|B zyPL1{D%)ooe{Bf5$Ri`cFv**nK~aH$L2;48%O?&q6uw^hc+Td`vqfcA8Mm_T*~f>s z%HOa5oYv0Pe&(&Kwjie=tK;%q*``DW$C%~E>P3DWuB)*ZnO7O~R8{@@nH{fkR&QCf z{#;S|sonCw7i_tC_SMfb73&YTUJm~(ztcalQK0dfMuc6Z*|979@pf(dZ?1oL{e5x$ zl+PKDl=7pKlCz0~oKdPJJ)b`5^xz&l&aSGiGu&oPWD^3G42SN*j7E zzws`3^eTeu*ovK7lTHOb%UP@CCE8nT*1_ND^1Db%NdL*w8Ih&4D|xklUgW=V_xClk zyHP*pp9%7inkr`ZC`c^jjP3hblBsKK<;B8(oj>}ky5~Y--3GVGkw+Kx-TxN;_3^Ik zN6YWd+{C~BDvPC&NBr#`2ag~RZs{+YUka=&_P;J-uyOO)a(Cdyje&*Y%`1Csof3mvSoU$c~iEdIr>&i`NS%|GQ2 z)y2PF`+pDr8UO9?zW>kU{pv6F$qN5FBH#A+^Z%FI|6D1z`Fm{t|7o*t|KGCbeR2Kg z-{t%3UOn9Zb0#;xd})R2d+q{`B|B3ey7aLbUszOgtlZtzI4k+)(a!V~9@BL@uHP&9 zc(X0(;@x(h-rMW+Q+;F2<3OdgIq!tD?@gO`E08c4Twv>dDWen@{N6-8-4RM1J+H^p7W( z$(!gsk<;y-ey9FQjQ{_+-Meg}Up%=}CABH2>GqQ|k}{1w8~C^WT>NqQ=E?qb30C%R z&;5`-8RUCldSjfNAX^Zd+HLE(5rLN*Bpn=jUT0t0dMN+fr_zH)b3XcFQB*q^z!|52dqJJ$NA^`GX|+ur}#v-|zeTKg~8`S)L_d%yQz z><_8^rT6#$&pvJPyD;<9gql-48t?X)>|CaEdZyde@bA}kPfdNXj@RS;y-Xo% z*~mHDeB>r^7dZ$#sZRPRv*xt@()PJaYOjc__~-bpPv8OrL!b;ty7TnwmaC69YCfHP za|_Ef%Uw@zb?>Nw#f2*`189q%YV6CzkKD)^RIQv?`xUw^lo|N6&4wGt$Ope zuSL@|mXulre)K8Su#WxyWme4m&DH<62475xN)kG!^3bd;hV#*i8H>c;tpD8fvj6(& zx>xSgettAtobM&#XR&sphG}Tg%79au?5Ftpc>9B@hH&w-N-M`N-1CN?F3aHs<{@S^G&+5Z2k>}_ARk`t4?^WS(>A8>m>vFE_ydJfE z#xz|XN984z608#P9F95%T+;eCT5f&*<$@yXiaz0{jxQAtp6Q_y2GF z+L&MQ&;Q!|AAj!Z+l9{muWVoaO#1%Qx||B_Ds8 zu5abp>WV+Xf200&@7ipCW#tsr+eMyTReyG5*OtEg8Zp0~!#FqGdD*|01$A+ACPqIB zHQ%8esdH;daL%HDzRa9^y*t_C{+yXJ-_V}v;Q1%&r!TL!E={#Q?IJ4qMRB^|zra|1 zM~04Po19`=(nGo$SbVNEq)*b~b&L?c^ys8k%a&D53gSVsZp<7rlnl(eFLybHtafk_ zcvc&FzxeR6=CCO_^X5jRcxgGOG3DA@vaD3{no{xYnvMVd3azRO->=#1oZV<0G&yBs zS=#HkII}&4OWAEWwl4Ag@#^8}AJMLT!4Z#Ua9V_v1fDPW-k<)9yG-toMZmL1b3T1p zv7x){lL9M8;2FbQGud|g3HfvqyWGz;DJxjdsV%c;>3oC0`QAHL728}%vDvpInTd_1SI4I2c9zSc#(MfZ)ITQ{j==)6j zA0F`M?!`ZkKRoi^^X;wdulK9}zm2#37*d<}eo|z*+5c}p@Bg~+|Lo)SlJEE4`P=@t z|L?wEvL8SA?)%Q2yZ`LZ`cv)&vMP;7nv45h?{Bj|_1kp6M%~W;r#o+$+)RGA{Hy-e z{OSLb{(s+b>-TQv;+x;{7RaOgC*X;b35Nm~)9EnPJPiI{`|zV*3oT zzh8d-duvs|=cacqnnwgm3a>MBgmy8;ow=7a&-Cv*)^98S1%BtZtDnIaw8^8j#&ITw>;9H z+l7TjUaw1;^F`M8Ny3*G59FS_@=Uf$5m{HVpxXOVLciwx^YzCye>q>b@9nd155I83 zzRJdY|MTnlcC~-roG21$TVQ5qDdCfS=ucez@%ZklyNPQ3CneTd|9Cu!e|o%I_6V8=roz*e8Ey@AO%v@(hjV4S!^^ z%)0wJ$dZrifaR1^#?`<6Z~fyQeX9P&f|EyLY>i&bkgc!CcTbmly>$JDhbLbip73|d zt(txB_djBt&Hdb2vvdvnrIaW0?*IG#>Gq!Z&ySylN81%&fBsef?R)*-s{d`b+1x#T zSR~d=)5rCuMcm)R>(<{4&1jA>ycn@z>6f&%mt21;vwoe^DXz2p`17l-?8cmHgCCx^<>jq{Edc6hziaOOI% zq`}ePx3Xh(DT6D6?L-@|B@;8JEjfJf=;;ZaP74$Sv?N#c`K7eRxF?HMcGrY$sIr@# z{=33tDfcynkm-kb(>N7dI+(vpcgX$Nl=9i7zxb2&@65Arg=>rd-LSQj{PTEEq|cV? zYhSLbKmOTu{hnVlKQxOeurg*XIr&+t{@dl3KTGz6z14WNCI95YBz?|XGtOnszxvhg zS5Uv*{SV&u_cJbEHhq4#Ix)2P*vX2`@p-p)Hhg?@QD$mb*sZIxcS^qW=ofKQ&bpNR zx7_FB`mfzvXW6frM8o6aKm5^z2Xbe@Y~`9J(Sg<$utB z_RDn7TPNl0baxUuF8_U==>zf4(&1-6XROIq6W=qt|Bn}+`Lfdg5kK#rpD!N0{I2G7 z6^Gqy^1okRZi|1pGv#~HA(l*rmLttQ|9%~`ID7x;*ON{RrHQASKUOXZ{(q}^;*;_t za=N>_m$<92C1+9`vYI8n z^^+tnC7-$BJbV3b^T4S2u6)<3BR5&QDIb~?^Lyd(<6G8$_6&SE-(>mC`hQW@|Mq|N ze;LphpME4e zdvJzMRbMy7_2JJieWD+CCGKI*J8N4T{5|X5{x`oays(;M^h;*d&0lMLOM|RhXYgIU zKgq|o=IZoc{l8AXdmr}p{k^=b{eRY-|1|yHy?Js*QeW)CI}3~aPw#qqS8QvoIFpdt zs(A6Ls?pSwn-@Pb3zQUTV0vaP#?o60}e z?m1BXQ-Svsi@{^=ioXBHW)w%|6&_U;es`MpT;&OMi{&>q%6Zf(y#Mm!{ptAK4@3Wd zz58$f!>9Ahu1uK1Zv9MR=HIV96Mwj^?^(ISZF)qY7>DjWd)vHv>+|<2pYmICmrPp1 z(4gY+|MAz769@UL(r5kB8T?@4vTE&=a?phU7jv?Vea4P^4q`8G(0jh;3*bVj~AFNv`WrUF zul#@d>z55DqnOxcvPCGjE?la6GN@6IOCyPqg@dWJc#BSWWKt{3m0#(YsY}i;_dr$z_WS6IewQWY;u{x$S+ms{L2~?#ufg-Z%U6bo=(@ zzrW0HZs;gk+Fa1Ngkgb;Tk~8t)!IML_B~zo2bYCF*W=4t1io&4vdZVe-uBY^hc9GruUyQ1&&NDI*=OT9otJmM{n1_hszT*uBGY8I zAk$YN{Xx6hAiTC^_w=iTeyNLX2j8SBCWcb;(ZQbi*aVw$( ztjgX5CtyWlZP_KBO@e@As>JEgB)vF`f3`SRNzSH`AhiAb*BS$y~0_MKNn zvl@4OGUXPZWsVQOC zpS(P=W7-q7*JrJdt(+T}a#!NDlO@}XV#U(Rnf>c8lz(;9ds{U7po8G#3$uO~#D4$x z&-eK!@jWj-&i{RIcjeQ9-rxU=c;6>}x4o41>7UChO~sGNcQ4#&-X>L$^yS~NMN1e2 z1RlI&G`_s#<&q7TI{%zap1&{r>?EcWA1=-)_MLkE)S2a5bRTb-dM(|{>-cfr>(xH8 z7I*Ja9PA=kBkFKxho%U0ajZ11@$}6SbpJk@P+QrQ%RFc5v zsj7B=@l=+sc0o?YW5xaZ!uqXi?`-?7I$4+blGM#NqUNQqCaw)X!j=^vjs>htJbZzEpFg~BR{6DMoRWS!_RF0K{^KkAd*#~xzB}2!ejDCh znr<3u{Hdx)u5Wjw^L_0@d)3{JG8{D#(BM307rJLfyTRpX$)kGR>pV1Gc33Q5G`V|@ znsZ5j~ zp=XQ#Pt6W~Rwuf8)BIKe$A>PLzdgSC>GjFrl@e`1EHhaq_PuI&`N)@vZ^e}q_r=ay z%l6FAf5WW?-Dq@F0lMWr5lzjolk&D|c>Nq5tip-|o+`Ti$)y>^?Pg=hr-Y zYs-o>om;jk)|9p%UFE$-y4zczywhRjGFw)}YXlP(q#B;>7Q)Wrvk88UYM@U6ZJo9>fM%Ta3-Xae! zv1e_XQZBcG^^&A51OkKt;+_RIfeY;U}Gn;BGQnw9xOYF~5m zlNo26LX5Og8yz$wnBh0O|~RUJnSFFADVpQL%S^{3R*2~*XR zWd)zUHfoxrFe~8j#?6NnB&Tn)icxvU_xz*op=j=`qZuzQwysjNTAJ*ZX?69i*vht^ z3+bQA_9j?+J&ufDQc`!mSm)}Sm35KwYv<~=Ue~uXcX5?$%XD54E3-o@=sc^!I?l}@ zTnr8eb~Nl1)e?MU8LE6{ii>fpNA3px>*p8TEZiaK&~Rj1p(?}j6{~XYrbs`@=rF0A z@LHpzQKjo6%PFlTLcu#&`CeCSV_Vt7@z|rwE7?jqbOVRmlI_P2SO3!A^yX*MVt*gycesNBl)#kix$N5i> zKfjy*^5RLZQpH0OD>S~IUgaJh=3&{_Om}Tw#SGJme^)_vRCUw_Jr?>9YFaWQK4tId)8Qs3{rjkLdhbLkbEwob{n zu{RC995}Kp`!pD=L_66QL^Zc%G)!c;(A=5lS;E$<)Xvy&z(6pdC5wr*^_{Z_qW}j( z3uj=1Knv3f14V%X$;Px2t2s6aGHV{5{QS;;#o=;>h9%v>x7W)*TKp}mH*5cibm`Cq zZ}O&eynmRzqM|MQ$Ml*O!}Fgy1e5PQQr*{@?s;X-2djR|G}+@BOdK7~wIA-<9K03b zV{l+kg}G?6Q-hCqUw!~*Xww|Ff8Nq8&#p6?{9EKybnW=b0uGMmH%_K2tJX~x-le{J zh1DcI3+2P~UF=W($7spT zTDo}l{#n1Jy5(6HcwXh4a_v|094D5*l}{QxmMU;?lsFl62&uMkzHIfVDUA?%yi#}TdH3pjR59~F&1vi*~u-F*H}eD%HNI~|z?mfd+4`Oe6xVjd@F!kXtZ*o4h zToU7yQB-XbWsDA(5XyQ(Ytdmx0RzdyIjd7^&V3In-Tir;)9$w>^IkQc_OaNo{$t)e z;lH!$W6a-`=DglJqt@HMuI}FL9q+<+PsqJiYyVw1?cc64E$cJCg6%inyBohz)$Wbu zoUGO~N6!_a*LfHkU1|=bn9N;n!SE*(ZbuA$Xpu5Zs&d-|MY-Jj@LxoyeMO>-7yhuq6H-<)=`?NN@*qI2Kp-HqILuJ1$C zJl$h+UcNhj)bCDw0Ppq87tf4W$IeU*RM%T<4_k&T?$))>KsLgT7<9j^LI=?eq^r-3fA9kVd6}`>}dRWi1 zKYQc9YgS|4M7_N4jHRZbA`)r~ID+P`;0W{(d{E@V6+B14A>>9U!%-gwg<#QE6XQSy zlTZwF zww!VN&Kq`Nrvw<9=1(xz>;1>keA@r6M$~ui(o4yk#HPD+9&cN`>9A+svU?rw4o!Z$ zGNR;+OLzYaXVVVIn^5uAe+D0u{0#=j8rl&}$N9Gl~7A*arYj^j!UObpGYu8s$I7Hxe~ zJ^S)>fkIV2ZN~)+90G!A>>8WWK6a{SHw7{@e3IaN&0w&tbEnCw$q940`nXp&2|LNm z+I`4Ek9Se6`^g&)9t<;5oDz~9gP0D?6?nvY(}06v)|%p+N1hA;Gx~(OH24cy7#n!x z1#f?LoH-ME0U}bxHfC*R8no`q#fIvHiNz5<-*04v1Wz z(xU8HsnGjSt$k&0=v;S|C6`Za4i&nxP#~k$%|&FoPq*-?Vg?pQ#TPC-+S8^B6;?PJ zvoa}ma;8eV*(ekpnxL^kRjuLG4@KX&Pd@KDx36zx7nc?OxRFbGvAk@&{f$r4Kb#a5 zzie;EXjaR{>p3N6W>A%eo3)|3=EJLp{g>?6p(bb1$@TOVTlPkQ<*U|gE~rwQ`!MWK z$l1$piZn{s+RckN!WZIr>(!Tr+(~_$ygC-l3On=nCASI(K6T)5t3S@q^VVPQYjQSBJjxpowUd8RRo4OO zFM01Ku30Iz<$de=EpNFK?AA-+UKKixn z9!JLEUoTC!mp?T>+s3@$aeK;~C$~@P*1r9JU;?xCx~1Q%e#~;;R_>?VxaH#h_BmV} zA_xC4^&K#J-gCcJe}A-jxYOypNJ*{VTk=+x*KV|3xvhHbx%um>tQP0{x$c;8Sxe7kM)4)t95^6WxunR7!xf%WEfd*hk(N)L8U5ft(8Xz48C-}>^_ z7mnFC+Keoo?=t_Bc5;zSu-|$?H{az)7BMQnl6>nHE3^?LOG>rcPCFl>5TD*sBG%@>TnZG7qc-a<}xRd4h4*B=#6UEg{l z*nUog+Z3;=h*h)Cz4})7FKvGII^(xbd-l#Y=l|RE`JPL1s8s8W_fcWLRje`@-cRzi zd@d#MZqK>W!>y!ufK0_x*iQ zw&085N)|-}ZTH(v0=3DdwVdLdieevBLED$k-h3^!;eSNSyRC2Z^J6aslXz!1bJpcShd*h*FRTJK^^ms-|e{J8ltL{){)kEFrd)MASQ9jy&k7y0uYH%ZeKY-6;@|4@{WWjx zzOAw2f6$Y4So!!X+vlH^|ISIjZ(my+;<;C8N9l{-yTjt^`?F{F6gnR%=bzJ5T^m>Q z%0fAON?)OU$T!pa%(ItFmsl;&p1*gMf7rP=??v0}t0o9%>&DDw{LK;+ZSi-j(^8DBFWyJ>U9s7EcTY&HyZ^d-&-9O4l^qF~ z!1};OAnD-azUz~(=G5PR@4vk2!P&k4IIk}g|FV>wzqI^sVA1N!%&RQyf1Ny&9KZkf zF}210iXsYcXK#sAw%rpoQ?ko8!{ht8cBxr@@8Sa+PTkz;@zhT)O*D6^nf%s}epmgn z>B+4EfwCO`Ug+2DSd@Q&$8ehHnY|z8u;2QAriRdH5| zP0fj;Arl1{oE~v$JaI2CKlv(esw6|3OO#hR=OIQMe)$vfePE3I~hD{s$P>lU?PkxSko)zv(ol%`FIc~~~7 zz{u6Kaha%B&E|-ig*)G^+wlFhLhY%{Z|D48Etkp&-nl=%YxT1v;mcW#t=ED|lNB|m z-v~%J_;d5`Dd%F=|J=-VJ7ryV#DcoRf0>!nd^1Gziz|w5y*U`_^ssPp)}g)1_ncP; zgf6Ts|HBuOQ1R||pUu^GkK;e?f4%v>{EPID*D4yTY})k8dG~J=x6F9J z#Nc6Usvy7hpYflgdpJbX&YAt5J8S=e*KKV#vr@jN-HX}xF!_$f!k*yGTUPI%A?~{> zPVL=QQ}6YY1ndM_o-(qUGB7zX`MvUvS^j^=r#ion<)2=q`kC_!T=}_n2E&>x6Xp$z zLYhkB3uo-fKCf+Ow(nh=d~Na9sPg;lelH5NpD}wB;%iyt#`IL#8CK4?1j~HIs6sRx&Vp^!qVOD)4sR-u3Jvmy*FgrCNp+Tte|5 z8yXxkl+-S*efY}#z^0|Fb_XsUH8nfX%)qdKVONs}g8)-3!(A@j1Cu1W*}{)}w*Qdd;62Gp zXsMNA!xJSN?S?rCQPuZ6_kVc(`)O(D&Ry5FUcEX{q$OZ|?Z@#xxzE35Tg3*|ZoTOf zDE8jg=$&!&`<;G2tIl=!YCV7PHUI5&T~(9Z-xK;)@z>OTEInSEzc(zyGEUL#`z?yP%`!XVKSBr|K>f4EHWtk79PKr^9346bCzx&mzuf^}4wR_Th zA?Hrl(%>~rI?+)Zj6%8$uH6q7F1xFm;XE3(7T~Ls^)3r#UXV;1c z4O(lo-sf)3xHsq4!#Stdr_PQnpZ8+YipU9<+xINY%rIDe`Rdo#?JMiD7KVN4J#x$vnlt`H1mDhJboQ#SzCsd0B<4>#lIUm~(&qANjvWSuVZa zJA1jz{pXACoa{E?o*YuQ=FX98(00?d z51j!zi7hEheE{_4sV~Q$0Rry zn2w0E2&$UK#Oye~fP+z?!9iI(@+sHk6UP#3W@=_%4r^5LS(@09wn_Z>=PPGFJov;k z(a`tIhRl-8G$ZB~MYB~&9YQ=I=?gs5)FfCHI9eoadA8_powMV}sYyag3errA{7y?9 zk!ZT`DCog80|!Howv8DN9XFg?`a9!@iHw4+cVNQ{yDrWZub4Dnb|h7#xhp7mG?uWf z@OvfmLiN-mv2U|K$Yj87n zJvw%4?u@zXssvO|{0a{Jksi|R$Ls&*?`)A&#h<%kcT8|UwQ1|ZcQ;@3+_s-6`$=lA zz{wexjnWot$0qRl`!L2_ILS0?*1z&^|6W@#yR`31|1m%6W%-*Q&lmj9nrlCE)2hNW zUpCdi9>(k^?;JP-#1?MYC7?C6VTtghUqS+69%-IEU#li9n9R!8`M;7=LDBK(`=Z7P zN3%FMJ7d-ee{JnF=-M*FFQa59*W`=R(|)O3J{>qEp}bzfv7=u8Tj-tzckItG+&L=! zZ`Xcx*;3Dz;&Q%YZ0X2S3){W_U0aq$TTtxrljqCH~! zQ^bNY-n={F@aenNpZf|g-n^gsJudmbP5zJSq(A?=C+APpYPiU&#(MYAVlVTZKUgbe zZ8+x|ePdN!EPQy^)b_qF@7^5I{JuZWk#m<|XT0c5t>6C3AI{I3`)iv8drMP(q}<{w zU*Z}Vo2KLyw*($iaW=3qXyofXWRN<`Xqx#n1JD3%qTs8629Fbf#K$Z$I+WXZ{R#4~~fjO-l+_JUXKFIB@0WIf4o+ zb{^-*zsj!D%+4>yFvUgBAuLI6p?C9f)o)9_<&gyN{m>-_=ENbfP#ACET;ak$L^^O|7Zbd71|H=Bob@-@}vP0n( z{V#{^{eD~Lt@d=0Q=G=H%~A(acRehsOB9}Qo~!fJ!>$B@m|yBVT~chyOpHMyE+G?%SNY#CJa(_iN6rV&Rd? z>-u?@XS9>vO0G5jthO{7$5-Mj?;C>|Ad4X=Q8u zviB@DPuh4*e}7@W@(Hmshehf(FV>owb@*+Ar7A}_lLY6fpF1b%OU#{{vuLqTc}9{; zMgH=7iwPY9Jw7~EcONy)(eYcwY-+_ZWsAm}p5VRb|EX%6wY50MXojBK@YO^(Ugbl9J|*B75v`Cakaga?P0U-DtS|L3v7Ih8`^*jt*Z zg$|Z;yX)%YPw2E&@QVt2U}F|S)X zq4_WG`Oe9E*V}D;B=vvGbJeTN2ZGyoF?B?$Rhpbt`5IQa{JMIJlHZahgBG8r&jDPr zX+2Fh7F_t0F5tzn=Eh{}MGGt^{9#Q=Hd(wSZPArWAzLh@dIHwXWBjJj%Ov2qa|-t@ z)19@Cn^trjYu&hehBoujdH>qWioPGY@l|Px-+hMFOpZl6aytz)zI!jx-e<*EQ*yv^ z|AQ~L4#osU6-zM%u&}yt$gXN|m(KrW+?366G08>gF>glq!~&z%AU3Dw%+*$|F6Oh} z{oegQN1E%H(3B6CbXPfEaCHnuX&n*Z!=dw%-I;ZyD$ z7ZX}U*R+HL2!uIYVd>4DpxczJbwI-Xy;jJ;3>~AjyK6 zX|-OR*5cdSlvev}edT7g=DUG#%ba!G!bNEs*A>H+TeB~%ozQS8PVfH5gj*M{eN#B+ z8@2MNZHLLWD6MDHr(fCXd5TG0bzaIXrOkHAjxI6ko7^TGsY+X0Zob!i^0J>74zg=q z5_RMXirG8+;=#322gE<}c&!mi$>LN_j8Hk}{@qM(_Ti(O*_X>sSj4izgX5-q;qLB5 z(uG^IZDyZO}@TJ@-iEwNxzoZ!^BVPkQ&hOSK0E#^rL;Sb+ro|&MrH_TQmxp4Z{ zUFofy2@P4xE13MYY+XGeH{|rqmB))^n>p6m2;Y6nUQxOBgd1O(klK;}y|m!w-Vbk1 zs8)Gv<*MjRGD+QJxwmXvA#LTnLTRjPhQ>qZmx?)1J93jGt9Ym z*$2CLb0uH8bGs^iHuu)JHI{KU+rP{-WoI-Acs3o**kg?e)QYwz>q$Me2@?{$8E(8F&WUpZD@H&K4u!u|8u#;&cax>yf0 z$vuc*EKt0vr?mfGvghWYxU)U`XJ`rLevoc?l<}|1Yw2xktM7%M&FlV8?@v;jx+(l2 z@2%bmib_*mn!lQTs*ub4_u|i&kH)jtXULa7emuoCXG!=h#jghZ@&B#tYT~t;ge+@X zg$~^@-+q3oZNv8K(Gr)7%qr6#e>|aeHL7K**@bGOCp-7vwoYCWxMLc>M93j`Z;mw^ zUEDUitX_N3!};>H(`R2^yjivHuG#Oo@0ho4vD`l=|Mst|RX&iZzUJ4js^7nHFXVKg%|iuGg%*cNQ&c@U8&&w!ngV4dIKx$SC8U<9bPH%# zczCw+%IK~xlxp|QN;L>$#qlOM?|#)lM&x5MWzAy(6T|JmHwZqcxSi zOJ-c!aNH*7Wod6zaAq~1P{JDdO10XLll$%eE^@wK?z^ZL2HY}edPw;Zwnv;8F!ovl!BDp&&xTa4NH`;mC zG<4}h+ggWy?JjpS5E^!c`ThGV zzn?F=zi-X*2cLzWyD7$X1>V@Z?YZ|KoBs2E%Q@d_KjW2D^@vDiSlK_nN1biv2i7Jo zM~9||%#od`@9X&PO|~jXU}JEKQmOfF^y7csr~ft;cW# z630E49aqJfwrL5dhq;`pN}rNYG(pZKdsAk!3~P|*yjA|9fefpE8MFG-_om!|$b^p6vW{OZ&zplV7HUtke(Lzw1ct8mEU>+@?;vyzAH8EI-4A*Y*{Ee310z z#HXz@vm$2-uqST*z9VgN>!*3b>kVV(J~vqQ@1Op<<>ys%BSWN9Rqrs|ys<9tWaOw3XfTzn5k92h0iCf8)=oOoo8A z!wi3e*C=>sD&#ORNwG-ittsw!`eL5{*K$^g6MUTFMI8+@9{P!0tkBziF#i9Oe|x1C zpEOdQE$PIOwJ&*X>tj`;IG0=1TerT7$-VpP*Pc(4UsrCL8faX6tnPx7RMPfaOJ1FM zb=&;^m#y}5`k$8urEg7*ed^otc5jQb?*At*;@>q%=(Oh-H9nuW>BU9owEQmxrri0j ze{IWO|Kng%0Jmp%($DkL8|7`;Zsa6 z1&s>cbE}9=hzQF1cA#Td=A2yxpSK?WH|y8q+Q)6rmxmpjrO$uE+Ina9+Nvv|;j*>b ztGdrVW=a3vpT0_S@8^XZ+veS>-~09N=HoAaE&RTrcIWwBi{JOh%d?yQez7`kbM0Hl z&!#Wy_RDTRx=$op<6AFV<+10l_jlRf`PBa7zT2#G(jnVxD}P^o+b>^tK3_lo&8&ci z14o-Xy15Q|Jt>&^^wXy;GaAh%MIEd>zU$?}d>`xdX726He6qg}o7)+19uiz2(kq*n z)|4{8rn=;X<`G4OH*cQIEr0gg?W@}s4nd*Cl9$vxHtTz^aAY%`(K|M|gx~yjONw)W zQ-HcdJ{wouCcXLEFBRinO`B-dclQ3?ZTo+${a^IE(z5Sa)mzK{>$60E9{r)ku#HpX z*3~&dVL^-QDi4;gKYjk#pQ`2GS!T8fF)W&U-oEkP)Tgv>;nf-gp zqz$YbEYkCz&2UgUY<=^^xhvD}vzOQJoSXl@KFjQ;$)qVkDI4_N%|(=_+=s;K zJ%JxoEOgkOUv1=S-+1L`^zo+R}r?q@yzs;VDo(!8Dc*_ z{V9L+`|P^I`(t-}+kV~t-iL?VE5H3nn9dn_t2tMh;Zfj|C%f2+W~lECdi=^}`TDn~ z#9m*zeCcG?vF6*)rXK8dHncMNIK^$D@|l<~G7ej|HBRT(ll!Uf%*>@^E2MMxv3l^9 z#x4%E*$#;?0RpSRz|!${!X9EZ|B zLBfp+PjBwsC;jB|)4Yi@3=}4}a4{JhG&~eHIVk>c33r|Gt-lA4{@?fa(S5bIpWURJ zFCB46Gl)2|T~guw!QIop{<>8EwfFz^osNekJXEi0SQ#iz)o|3|exqaize7V-7x zF1tB>{hxPL{!T8@e(>QY=OK-mpfNxUB?hb9qWr`XTSAB0{j}J|A7k+74kSOI}<@;-sn?Y3TmzY0> zvC0)WjTS3RS++#?Eq%RQXRSrpjqTs3JzViMwZlH*m0YGn(V5K0v&8THe)CjeS4&Gq zqu?ej3B?u#HUc(da7*uQHEglWfhb6Iy5spteBPF z<-MY`^v7naBiBvs;{IO0efjje*U6yuCn8^qg{&Sn&&&IzEHkg}sa)CN^=FSUGO!7H z2uRI0*zvKElXu@jx30afzq3z2ee~t(lV9}1-!3+rq;jX#@{jdn;e6+lXC}m4zx@5@ z|CrCyeGi-TPAtqwf9S!bCC=Ez+8KU~yV&R|)9#RyC3ok2{(S#h|MSwoKZaT|^2@&! zXgT`37`A0Pia2&~9BwMTsdGi*w4dnZz&9#_$**?nEWSVQvFP#X6`4G&O_P{ZHZaX< zdG=T|H2?JVH#a}{y^3Cb+f952b9c9#@!Zw#?d)>p^6g*AR~!nc-TK}>``3X3)!QVV zIlt4|`+j=W*V9}7o2+6m)^PC_^sf5Z`}Trm_a?3IXh%0jt$>79w*<+DFZX==sw`P` zBjE0=2``17uIBhT&FxF+{Ppo=YkvOw^DS$SmQ1F4aN)9zGcLtUadm9)c%aoD_w}{HzV8$}rd`Rm zy?Cj~iB*B0eRIj=>;iG_9TW5nZW#Zbt?s%!6gn|*G3|Fl@jtx8vW z{qJ9|_Lcp&vHxA9b^lEE&kv_3x+aS8M@-QxKCDzDtGq?_&69bRb24vRv}*EeX|Sks zYD$XbJgng3GQZJe(%GaV(c&=4p~~cxVGR2MzC!+UQxAOLs%+`Jef;?L|L>>2n|Qc6!O;}^S}N`|NIV}?@Fa>R{HN&obmD@@6Y+M+9hjbrk2*tdJ_9?#_9!)3W5@0 zT_5#kObgjuePhd4E5qr}YmRICyl9wlREC2~nZe>=lkMI2{BG7}oBF?5R^Es``}*+b z-^X%{qgFJ>wNwjP1V}LK{ajV~)Z4xPOiQ=cVi%93Wnmi`xR{P=tv@s2fPp82`Km)- zSH0evpDXy5KiPmq?8cW(>^!qCu{(X2FuN2bt|-05K+w^odr4>n?~y%Rt7})Rebm^K zYUHG){d0QE>^L$x08SM=KQOYmpY`ueYNx=PsW|J#8kWA&n(mLl^3r$5mB6`f8}%Ii(lby=7~gY zljCCip%HZB{LF82EZ0`+-Ko8QSAXj6&H92RC%sHuisUXuOyB!rwel@?9yRNSTmAi8 zg}NudmrI!(w@ATfs^XLm7V-7o-{Xv!za0xv2?!Lurj(d2yfUruoSl-~BHN3$~xFsj5+5ZYEt_wqD(a|LDuj zGt*)YyT!Ke%)Ba`DQ4DbTDXMI_YFHsxTnHrsnwq(8HC!N7^%f<|9JYw+S|*YEs4sQ z{mp#4`J%UNyL|UZwVYGupX2*tW!BQRPYy9WZ{1hsZ$0l_bLz8#WW&KyS$rE-tm`|N&2Y?3RnlO!#_=;#3atg4 z4<}Ck{OX2>d(wt$t`f`dt%&ShbZXrrwC<=YIzr zee^i_#Mu?*ChGDvc82p$mDN93zqB@^W4Gn|Nlzxf?Y*A6+^}y>J9}e*LHcP+>+cOe z=j^sU-L2wRel#uY=hO2Njm&3nZdFoK&X$ z=@5rKHWRn7CBM4P#VT=}Kh#}y%LT<(k1|sZH$Uic%zCw8UC5O#DqIE1!AA<~b>bmb`PaSH@qP^Yh-vUS;pI=bo3hFW>#;%RI;1OnN$-_9oqE z{fhnF#*3o|0VZqe1<|B}Vy#wH)r3k}J4Pi_8xHu~d+v?;&$ zmY|5n4+mdlKK!F*>oYp-!OC^55H`Tz4jQPo;2zP?MB zBd*q6Y*M7eiIW7Ef$?c51%~j{;3uRx5J0lV+_*UPbJwbw}k>lxm9+|hQEk9Z|&E0KveA|H)`#xQXbq#ybR>lA4&(4tVzmmcfk6+i!iZsvq z()nh#7VFdwvnwlNg73cjTX{FF^4_vk^U|bwe|fdTBbCo2otw9&`hWD^%_)XAjVx!* zF|OD&cbVVwqE(^H>&ow2?R)A{I*qMfB>m@C!!7lx=ARteBNVnR_-=jI!AX|yTZy%4 z^6ZtR+wVRNf2p+LqzV_qh1zM0^^R+q9AvLB;C#AZ%J=vEdo#9jJez7eu~)?W@y=`2 z?SbOc1Co~JMIWCgJe%o#fTIFCD?8VPhb*E@4*N=-cUE1BeO2}Ur`621)Fo~o%#P`- zSwCMuz$2?uj!|T4nx=y{BC|JXu9l z6dVE+rEGe#rZ01o|G(VL|J}yc*$Ojv&XVpc`4e7m=70bH`v29deeN_1^cOiyGQ4K` z`W0jQZ~>*ZvwQFAlAQ8y)562=r%PYgdtoei>OB`p<)R;sOgxoPg%)a&0jD5d+HluQfuJ=o>f@#?~tr%ZD{SyTtf&M=gPc1GOt|kb+Dpw}$*%k~=D-_f--$(v9FyBZRXR_&yttNpx9YCmo;`nyGTTx& zN%HHtUruUyutU9JQgGxZ>8ec}DvOULGxxfhM2lB+@ZRF(R%EZ6bN%>VoAc`1ymc+7 zb%sn5<2>ddvd7u@sAANGtn$0ZWlsyRopM@mXx7K8LV*m@4bdDLPUa0k4>&j#+mbR^ zoI3@Xw`XX7|Lpd0g+>2415Fht1s9Pt!A40|hAE%p-iflWz2nET+}Y#jvl<2|ef2gC z6NaW44-`0NT&P)c<4Vf)9nQVy6Z-g@MDupV%t_=kX=zAeIxwM3QDEx3&ljZ1N>5#W zRhvI+S1<`S)i$I`Nlzan^*s6&)&f*PqdOr<=d$$?@ms=c#KM>?=F|?%UG! z@4xN3JAKyaU4;_=)arG&ewL~|xjCLQ(Km=;y2S=XzU+ozj_jWj9UNDj3#)Z=zhzhK zeZ1S!COe(Y@7=W%+l^G^ye1x!a&14IX_xoXC93)P^m`!{UlQH~%vtt;rDoU3Xx8=4 z@@qrwj-7a=>Z%H}cNoSDC7_t%8~RTGyV-H>~w z`rwn`S$C&9ZQ%2J_gbrSin)J$YkRGH@?rgXM>@*S1hHv4L|PqYwk?jH?U>NGZk_Vl z2UBZpf}Y6FeOGPcy}n7gUEM;&L5x$`XmZ-e!`gP|ju}Q>S^fC#s_e-X(N5h77T${| zHFF8IX*WL9DLQYE$>1u)zN1snZ=*vK`^SF|wpX7GUaIzZh6-b3c5F=VldwPPGAbd- zPnY@}c&W};#rElIngDms-K*UT<5btD$=dd-O{lSbGeNmY#@S>>;DR~tOO0C-p*#_HP@U*CV|@ySKI zWaRiJ$_32mV_DHO=_~IC&ED_V=f-SrJ9l%_p5i~kf4)}Tku$$`)Z8xXdWZV??6ibO z=jMF*9hrCiK>t6(BLBiFllF+MZzE^RKen6OuFE}fwbhDMORnU8*n9b>p~;u8k)peQ z?h0MPe1}(}b&cdigBF*FC7)+}36~Vxq5Ust$tPYGj4=uicDb3w$DnXx0kgpLB*$fmZ+FPc#GE_*aNqWKS~Jq*S_5AnxWle8g-3Gb!Kl`> zJGpyn&6Bg#b42+v1S^=mT|&1Uu1z-$b~za(`|ix7-$`leS3Hz*W;_06&fsD$`KZg9 zHYJ6#P2fgoZ*s7{LE-Fg1Xp4cBZPIhL^_uW+ZT6m3}{M)~emb5)~c|0S# z*3e@2>0O6&)1~}Ea^IQ173nQD`n806+3rQR-$$FyD?4+pjd``cwUPhJx~l%tM@9*j zaTb=V(>CwsyH#uV&@Gi~?e}kYb!J&@3=4j$th4b>^|#+ozpmPSXSwyZ^{V2p3&Y-4 zlspM6a?>5A9{q{?ew@v!dZcpU?^i z+b1t{r=B^O<;$ljoZ#qk$MOTia^;RaQjKnY8=h!9^Sh+<;ru}DI(^&GQ!n$DO$$-^;Wabp zzl`d_0C&a~fs(0F8%-0-ISx+a-*QMK?c%iTr0ZQpwHq(H9tzq!>ENO%THH@q439+5 ziJ9HR?X}BW@u1H1gbP2WwI&N3-p;8gYIb1hl@~8lgL4BP6vWB?vf+LhQ_1ZW(dFJ^ zEjO*e_-y;i5{0Ikm)%#FJk)D8>sjKd=F|}AXK41P&h8SA--(4QSXlU4|EZ{|1a5ge zqq1aj>Y^UbiG`b_HTSONwX^Dyo+{1xT=%|D5_g_yyvdq(u~};tOSKs#mE~3(-jfql zQ{=JODgN_5ISZ{1dy`wO@x7SnYWA!fWXU?JiCA_Ng`VCd@f3 zV^AT?9+F)7f=znKlqMAshf~_(p?@#){I0fFTeIR+KQ@N?m}#sB|l{D^q9=2OS}>lIe}YVViaC^&G*jN^iWklB?zhu`;!S>9c{IC+`0 z8sBk=gLn91?R`1#T^{@wAvM&n50{mvg@?3HDi4^`X$CZRyJ8Nm{K7xjHpE z6h53}j*NGShZ{|7+?s zY5oPaat%RqbBYS3R>wqZ{;|2h=^*=xmyzYG z8pJFq;{RpDDr?pCUcA|#EAa^XbWQc&ycL~+Wp6$?vh#+2H($R$_~yC(n)$zs!d{v6 z%`lsk+Opwj(uQ5{W?)jFrBH!Pyvp|+-qoyHrqSHAlqFT5c&FFurka25Cp#H6CKOEEuEX>o=;w5fLu$|0tn=?Q{uQnj zYq=`hTE6Bc;}(nCK9g6jS@WsyVMsy0WoF%vt>5IYzbpyQ<`Pi6=PBFp)=mCHo+rbC zki&Jix61RnzTdL9yfftSt7C_rPBS*~X_81j;8tc=WK^xwyl$e1^5&E(w?r3?iu-K; z)BaSubVc*MGnZjmAot5jqnLB9m5M}jT~SC;Qs+ahxz}t0eeA2W5{|HRPBpu=aOuekw$_dV^@px-HY9j5+&dYo);eW!K=jHL?9^ zZod=D;PK>3Sk_0&Bm6s#2=Yh^uLyHN*61p1dD^o{^~I`^;%!(tNZ%7 z)wirEx2L zOm=_XHGBK(Z&SYa$?V)dt9V|;D)Eiq|Gll9c-r3A?2iZ*|T@gX4kUyO>Le@?T^GRc&>6}SQoDJlMok<7)eh5-{+dU8JNta7QSl!DcNe7C7<(b%E<+-1(hF; z7qJ+dYitb6VF< zLetHC&3d0!_}{zqzUJlg=A1tvi>D_U&l^bQPTbUF$8h7R z!^4Y@P8m&5yS-R9h}C26TLY1a9$V{QSnc_o|4m6(V4Ji;Rrm>ZfxIn33<8T5Jg?~Z zI?tHxEbny%#h#Ea>78879T%1^-C49{ql~grSg(Mk-J6=4b=R+#ul=aj)bSzq3QwL0 z%l7A8_Z%7$7##CDW;=O*OEpdPSIW?nR_xxbaGUYh8~3G7ySBUYE;;TUaPp2r-adiv z2hX}JxRH12)^}bP4X)T@i~kl+XWqvs6J9^-*Yd|9%GV{H%vyczl*NmcKN)2YX_(|c z`@Tl)LgcPj+RZ<2yn3*;Th?o<#&xYTU1{E*9A}9iY`yz7=*i_fhl05)nq{|&aB5_? zug`vR^T{j=Tgh6B1fh*Lj&5k4`0Z9-ce`HD`Mve>?#re;Y&!BFHs^j})nV2kht8#+ zzuw-Sm%H9H?%~v_UHce#g%?z|FgbawW^^*0-m$chbF=){pId6H7Fw%@x5Io-KOW%5}Ns+MfVx)p>Sj{h!}%{*!n2M@-xU7cGgWi#t=!35kmIMMy9n z;xpbg!6f(1dX<~+{yF{I^}cyMzxsyBN+F9l8I%uf?~vlyX0CCAgU?a2{ryMzuG)Qi z|7^Zjujf9-y+iBlu|2(MQnQ4D#KaG;?2KC1v+(5-{)mPpIh*92U6#x-E$+2>TKx65*H*?12?h}pX~T5x450?+3wxh)rrYPd zfBEk91=%k%FT9-Kbmh|hySIf;-<>~iu6D9Y{j%cv>71G@w|xZ!M7S=xO4U`?uKQuW zJ}>K)zWLvpn0s}X%y$>f<~o0U{+H9U^J}`A+ux9|+GCA^%XmH=5?*@po5q$FKG);wvUjLG^ZDGZo|rz@SBjrD@$u?(!RKGju8Od{&CTE#yp8wBsVzN@ zZ~XtfJo+$a=a`(FF}fg$or{?v={668`R|wp4pxVY3s1+K|73P$ zi)eiNE7j}HX-*z{SQ+;$J^a!<#!&Lxi~~!)a~ALQO_+S;*W#|yRrB_RDNQ+SnZEI0 zV{B|@a#~VeVrwGP(hD}zcd%SGkNTX;ov?VF#PyngAAbGJfBk#={uViI`?x-tiA5?*H1rb>Mbv_er_tD z$RYh;Szd#Hg21WV-`;;8uDpAX*S2r_^}{#A9`}BoT{PWpN_!G#{f{3JOEnq-9Hzy+ za;(1d`OD0QF_{(fO8+I)p0}EONtMlb&#iw~F1!nW)gz~1VE**5d79=Fp`{9MzjXfz z{&6;7^Wu)nx2BiIUfOfdZEmq>3qz}gn8OK{ZH$e_c9_g?Shk5n+)Xvv^yZG_gf*Lm z3m&Wbl)roRzPD*sv32Fn-P?KfYb>nyr#y04zcM9nZ_J%t0r}oaE=yl|+1h^p|Kz;s zy*cs!&-(v2@cwf9fsvN0q zi?3h3)#F%JY#(90EjD9Q>fOsPAI<5%V0L}7)AyWZJ}tcEq5Cthm%O*Vd;Hsi`*#?> zM<__<|9(_#D}DR*_wz}H?_%xFIV@An?`sei;+>uLocot=(d7O1Jl}5>pO&}paW|EJ zomam^=5N)vc~jTjll)(BPW*ZLl~s-zrW3q3{a*g{A@|+OX6p<0rFXl3I1#kVt!1ZD z=yanszxm2uE}P@P=y5Oi&BarO_ZKMt^xRr`xAaCj>*BSWcxI~po~1o4Q|B|=A>Aaq zpRN5YY*l$rn!;SY3KbVcwq(0SnCc0L-rTaUT>08;SB8+63-8^!XdH4|=*7RHB?Z+* zi91`iY+&i*n8<7?J;z)mph3gv>Dlb{&sVokpa1`fvfTYG+t#9~ZoSu96?zXaOlLew2#aFd1e2J}{f8i?I>7O4qeiq2?WKo&U@~4p-|$mWNE9(iX~Eq{+y_}qokj-UUHU0;In(5PdaKmjN|zk zW_<2R^qjhTGMU%rM;m7@JLRUDU3qdA{}L&7zW;vz_NX3IaI{jYdc2)a;$3_hWnfvEz@KmrqQ7HTmngV1owM zmlu}Dl&%sqXI}cW;-6LByY0tgYu`UV9Q-fi+UJe6#(QP@qSjmL`yGC5yVt(T?wN(8 z)z_EX*M(Xwd9W(@{)(_k7atbLJWkYMj#$9pJ?pc{-(_#|>b9P;*kbHu;|?U;bpG(YeFoeAkPv{f(G@eCOOtj61l)-(|`!%khoQT-+#< z+w*B|!nNnOZ`5Dkw)F$oyRBTTokdX^jO)XC@JkvAi4BM2x<91o+ zt*vo}<{Jf`(bhd2LLEV*-7 z)68PU<_(OKci(tY5csV9@NVG}{k@zEvN!(u%f9&G^Wx9lewNdu4=GNLW06!{xy(WT zFN?xryO=e%*JtnP(%-uK#Oy8SZ|^<-xxuoj{n3d+^Bwy|JytWEGL$&9W!1yNfFHB& zEx$ayclz`5`~UcUnEw1@_cH5k&#u1ap11p@>Dk{*Z|$!4OPTX6zkbqxf9yX0;?kKP zc|sp@-m=m3e0l5jqCYV_bLUQ0G-;}=JO8qF&G(C8j)j(nv)^oSlCoSAk<o|72aE1E;l<^bMB6x_X=w_+D)yV$&>86X_5zvk|2ktatrgFZKY`@hpTq( z+;Z;4@sC9T&dq%b12gtN=sKKG-Dz^{%E>+R4DXkvtkkL8XCxi$BCxhc*?Zok(48B& z7R_F!bdc9m`kL!M$##ngj=yb_cDhJ+ncQK|Ne86{Et7o?yarW z+RB`ipdQm}zVgt~t2@h%trECq{Vm&L*H!weOzphQwKUFl3Wwp|_&myNoIaW=rIz2hHIri}170(|X?|Jyz|NHLi zwHA7xPbb#N<@vw9{QKL>(jD=~uV+;S?~Ux+b#{r&yZLji=HGjoyuHM4PuueqxlFQW zZaKabop9=pi>eO)swGuv_0_tv(szo2)~1QGz2NBfnz8E|mqb95!4Ho1%8n3;9lhLN zjLy8A`ubbTa@`&Jo7aXrD7xutDtT0J`fM=gs6K8c;sutBKPjul><~%v!$*mi4sq&XpH!aa6>l>ZtcJ!i7y zcYh7%-?jDI)Z_KL>SpvE*Zll68@xyM{d;gb^fNdAHl5?|t{p#d=!JmXN&`+! z$FnNFZza3gCcP`Upys?))@=IYylHb}f|m;gvA7y^{e9K`Pf;jvQsTa6t`$L_ZuZpn zO73`Z)4FNqG_5z0$Z7gU(ax-1U9JLZyo;pfu?V(! z&Ne$yy>hd{oHrgy@_RZv7EYe_>R8>@B@rL{d0Q0{7CLv7^Jp$fk_-&msS(P+a51mA zW%92n&w2K)yS?W2;irq8!nsDjz>#Ey==ih#Od->;o-)bt8EsyMRu*gelTI$`(vK5C8ZQ$nHo!eG&+ii7bH_xIyVO8yb&w6ci{aWu+Lp9P0J` zy5tDsoOna&Rmlx=mA)6|1Ww<3i@C+D%W=jJro4s&N-s~%<(Vw|KWdK{e`GO#(&O&w zK6Pi0>gjIZ^V4dN{+CY{$DjMZEnT$E^(enbLw*R42q*IsCn1GycAp0UM^uzmHxlHG{D5)!= z)RGw$v{mX_c5#WB!pc|2gO+4mjei(9>&~i{MNC@VCObuLWr?0$yd~C~O;AxwVO<1A z!;LV5$p=?@pY_>Qv%GuvRo~>7JWPjMPsiV~YpapX;+VQBU=5F^Ko+Cv(ua0OH@)MU zblzn4EEi!0frbw9i>B?l_)zxZ3K`Kgp<6d? z;F@KT`K)ofVZp=af1+%TSTgk+=)^>Po;&;bo5&oFA1#83r|opuI1Vc~-N-0B$t)~t z%RcQw#~hYzoa}1OzFnNoNxr3@vb%n@9}LxAEvz$Z-OHwjhs(SqxLK4!6@5getch$n z#du5gV@LCj3w95S{_nhGeBJ!TEBosk-B_kGo1UG0^2sT!?n&8eEKbRZ1@E%TRp_1T zv(e`C299ITHub47av7L-Uk=Jv6V5htA95ODjxaOPa>3(`LAUd0|9&s`l4c9&Fbcm{{bkE=xY#^>Twsv&|am5S2*R z<7R2+GEcRpRUQx0to`xTM_F|H)Lw^x+l(Flifc5Krm)1YDG10jIZWYS!tf&7(sWZg@a}8wTN!;_*#-MV%YIF`&eLx4s)4b@@PF>Vc!|@$g`^(dTy!YK zCnRh(*FWu#?oZ3_zAs@}@uBHSuvE9@si~didonHMm!Ik1a?9?J#yNrgpN_c&6dGsr zSa(W0+w6QR(>SJ%BUW$~v-?cmVBsZ#>eHb;g>?KpI6hR(?x z=QmM{ZPqPGFaEabYGR95Ynw`WP{05DeW9!M&fRwAIy7t38Hvfdg+~}CM9zBRxOJk2 z>&76aDIYvG<*=CXOx6(EIYa5$oC8UHEN4Z7c6e!d&RC?%uOi@Zj*U@ZGQ*Dz8VjEA zwlxO`ybaX*pb&6w@j+fyW2@4Q_YK-tFa&bFXVHqUP+6q%EJaC6Of*A_TPQ7FV~T&_ z!Wk?Z1r;?XFdBAASl{^G9KjZ#A^AjU--R_U6PgP|nfeP=LpTZr6T*KUuX1ZOI|C}R}^^mF3*T-(vb+|#6HCjx&>3K%{n^$y4PMvxn5Gbm7KuXF*WAY3|`k0jZd^?tsJ~pw9Hs8b)z8g zNd`k;dq~7It&39wrxjGUZcy~_(tTTGS|Zsi)!m%w7;E9s{8^#VC(CU5+pqR}_=O#~ z1(-LvJYZE9xbOJDcwdVbcgLiF9A0fL#>VHbr#%r|YO(C3;cBO-Ne8A@>rB4AYfDM$ zwuz6~mae@Ue)+;Hk+au7*6!l(mrSS$pRMj&^KC)ywEI&m*S0q2<>as0^?#|nZ~BLM zDp!gxrTFFbCW}otduooY;br-c+`kP1-m)Z2`p_YMchCQd$E)iY>JBA)&H5?+Q|X|n z*rT*omh_H)*N%m@7}Qi;XYbd(ET6{nFtqOUa%sPL8+1Q3T`E}Cbhp!EZTQ8{r)G-? zEvxAW{A}o?(I9;Pr)z2De1jCn#!SP95`Wr`G9EtWXWP`=WmaRy=de)kvq1*$6C=~Lt$2ARK9Km%>)7%?_RsID|D=m9?r6_ma-p)Tv7=;z zdyr@RlZEYXx|FLjVn0SLtoe03{{Nq+y-SW~WC~V>>|e@b+i0_eXSJbX=yczkqTN%L z=s0P`2SxC6h$!fV=G4?wRq|>&)#n0}<;SUER0ta(+nY_eN;fpELgk?K6x%Kb88dvWVzUa5rw>6T@ zc{hGP+cBqWq4fJXFMh|As~t7_`=WGhWv$TG%k#7U1svNw<#6ly+TeoE|FY&~oqH;` zarf!u)H&6S`JWwfU!8njH|4k5*W_ERw=ZYBD||Gu3XchgfyVm19^Lg~ln*Gd~T#j>C*z5uhvSt+=?N2l~ z-Rf8Jvgl}TqDp>#T>43-iz`mueC3m*;;yJ0wWjr1|I{59uXCy1aWV0i*c}nWA|(1e z$7br;2kcDV%fn^XTUI;_eR0%hPPc%mRH0aiywGPIrxlBQ7}mz9ZIZgHE9sYd)AsFu zGM_T^Mq)nrcnLw7HxyfQi?9ky=O)J@kWICszHSP;7-cH4PPDJ#t% z1_tHL1uw17>D+j8S&D*c3cki>elGmU_OdMEmKHb8^3@Jrdb}dC~mJHzph7=X(^!R%_O5{x7tCb^Ym{ z>o51cJo9tn{Wq@LBY$7;|8H@&{Ii?$qm$03a(1SE{Il}+OTYVf^UGI1`uy0e{{H{O zfA%)B>(^M8EkA0SE&1`;*Vntu`}=RomtVie*D~*0l*%Kg3AJy#>m~1Z-@kJ5b!tY3 zg+O&=t9kvO_vQ1~-gsCu;g{L6JC{~pV*4=n>Lv%a#ur>+AA%XLYRIZ+S-0%>s5e~u zCQl(&oAE)@6wMW;c{v&id>^H2ADBO_FtwjtpXaf|fI)rHL;+RCb*>YYFI;M#a^vu; zKelfZb}ka%bE(_-{^E@ebKbD!Eh$#NJbUY^DIytbv(_D1)E_#>bXRfc^Q5~cm}YgK zk=>l)k!k6&V&V;}bGxv+!Jz)N4F^W-YwDn$>w~&vrHYFo(|#o06HfaM!x*CW)(N9nYT3NRX{C zl=$|cRA@_<@Je-M&4rsCVp5g5-^FwK|1U&xszwpEXf z=igm=sljvhV#a$3(#LkMVDNqIZW_KOq5D~<$GtbHN9X2wJuH=fxpv0ae~j?~Z!8j?22Hy<(X%L9JSIPST1J5q{sJ9-e3B>Bt@M zNl+9v+1hkKOE|mm7|((aTICZ1o;k3ts+KCgtaaKT)GJVs$yb6wW0JJO&u;9m(oofnju*Y3p_Hv=Vq3@YVDjPq3ET~_1+ebW}`P0o` zzZ@=?*JLU6Wq--MyLDmGZv(%Rx8_aXc4FtdJvN);!dza|(N{!g#AxC1HRyMslJf~AjwX`7ggyi!z!TzCLlaFR!{Ackq9o{!~~$<73_l?Pou>3VWGwH+62A{%FFnM-3lM4DvR0W=vs=RLm=Bp8dS$ z^0S3$p&ndTzq`tL&Sn)$3a>FP=Q=Q_v5~*|fWTd^?p24}U!S^lDda%4TdJl*8X!6)LNa7br@t8`NLh40n#42!-&|}g3A+L2;aM^rrVh66nWb5T&t2{tx285l`jfX%?1;b(%c{0c^~Y` zd()vY&*b`-RmF2i=3$lw9JNFx$pwZtx`C^3GEYP8Tnh@ala(sdoI? z>QAQ@e)r72^<2QAqe)=uidqF9)^qF9@;j^)y-r15-_X3$Cpc!KTENkCt>v?&3!Bz#V7Fim|G-st znElV8^^0EZnPHgqL^aQ%(&@{|+Lq+9zVo3!e_xo)HQ7NR^TLe$u!u(r`5ew4w$(1X zWwq~K{QvrG{>P8|&N<~?pz{6YbN(c$!b{ahrDhlOUD+I$=Cvwkiq_FZGhT&cH+$<{ zzF&XqZ043@Y*SVjXoZM8^L%6?$;lZo^UXr>>26HkVn37Wrxk6JpMOt0eBpJv^VNaQJaN<_&B|MItu{)-Cd;3`~Il#oQ_^hJLgGb+tZ?z}B zxfyEIDB*0ts9TUy=AtaA$*3Zcx-xDXfBx$uC5HRHg=D?DzKNfykH2U--}@U$etuaQ z-|qB>A6wvax@mRy%v-7}(|3y13qEq%!kvC%&B3|aVMTn8Q`~yBYJ&gY{dnikzmvj~ zGH&w$Zw^T_D;cE(^d?(cc8uZ#5_(FK6ACEVB@a*fWvyWNNF1lQN z>ZRU|W9L&_BBpGTS^DA`$Fk;--waC)mSxoMH}F4udIrzT=nBDrhK5hQzpGr|ywi`2 zdb(e7rEoLLBo|ZVfvi2XB`9JIy3eOeV`t4S@{mGY73EN$|Zy$0wa>#9w0#~KL#mv(c zS3WNOwR!#bv%loNzLhZ-4EkdhQ(|%{Uwu))4Nqq7nKxV(3%@;ajd#)NzF@n*D1T^#3bQJu=tC^CanFnOl?O{ZhuoI<;|{w%2a{^3i;B5h62 zlYNaVqqeTU)v4wr#brEM#@JJFLsm&@?Ud3;&m~)855#uOjkqD`zd?_0&aN|FEK9F? zUsv5Zm1~uvr$FkZmhP*uQx$|(I$UJtT(j~FXSgxzSxzqViyj9GRyuG`c*0=enImLp z;$pyJP{lU=fFVa=Q-k-})V}A>U)bHf9qfO!RrPnmJ z(s2!EPqE}3nab$A>80}C)fUq&R%)2MUDeFW9_}Z)S;_xr=gs9@OaHt)cC#e>lupdO z6@_&@ac$GDKRA7{y!KL8>S~c728lCk85ovSSuizlvN}!n?pdOzB>m3oOVg%BLXSN8wL=ecKp@7%GYdB(RYCAsHPxsNw3znIx2yWNY4o54eglhc@sV_%}d<0alHlUkA$ zdJZo3Kl7Kl=k#>m#X21RPA%=S&LWDgYwmyEwElLJSQ_r837y}&z} z`F=wlrl2TtL zZkwFFYH#?Wg4TA8ia>TJ6PI~Pp)sx9&WXN8&ILE;+D6Asy|bG=(j_!WO_6<3v&2kNy4g z&s674Ui~EcDdX>c-m9BWAKs-rXLV6*dFj`=eErupAO3Rl+nvA7`8L0Iz1me+*S9}0 z-1V^sN0{WICDE={4xB$t!gg1v_3afGdYp1(hWGo5b=B)%-=F?nZ{PL1vA_Ss$s0X5 zzV4>ovo~8`P77w%N;baKJR!Z1#bL!p-+j#o%p-L|Z|+V$|MKUD$3Mjj9^XB_>+M$a z^HFORBN(> zr~2IL{P(P)EiSeGO2xD))qbCkY#O&X+2$NO7b2PC%sk6?qm{L?)``${7k5`LHF4#8 z{*p;{#U!^+3r;QJ+$`WZnbAZ*=uMy5s@rWVA51zDTRUUk+1nL+_Q)TTF}YSL`e}uQ z&FM8IoBRc0LSGvkb1?nJaQGZ+<+5p^-}L-$tjrKTyz9^ngJP#u3pQ+eW+fwXh|l+E z*y>YP9;bLHPSntyylMsC^5wgP3SGm6g6>RAz8c8B(lJSEMaoXkMUfm+q?Ip<%`*FG zwW_F<~e&fjFlF79nTKj-{E_ph&_ZitjdPE)w=dUTHL+6^kd1g`ijTsEy^fvhCg+vTh& zZb_@%YFL+4iYNvpEvay1^mtR%)5`B}z1=TzY0@;qcaMHo+CK593(bjrWBB~a+vjuJ zUUl)j`C~c%QDs!l6V}xyZ!UzEy$q?Qh=C`Stwkua_U6o}I&No}0Mq`)p>n4JRZr z7#-K-9A-GE$lLofup2sG?G~IHcvDlWRXDwR27PefQ=9*`J zT`AtWM2i3U=ZE#P|6YFiqh$Z>^c~W?iR+#it=0-)(c(%hn$VCPFM8?rMwwO9$}io# zn_9hN-^=s)zc0W1{Mr2ed;frs9J}6REtlC9>*u7V-m2Qiv)FA#hWgb@MyEg{pZGKaag(KsgQw6K zzj40tf5o-I>aDrk-aOkGQ13L$xpT@2*ID5{&kkhqr@B5B-|=|Uqp-r7StmWdzYd@8 zW9TksyjiH$)2@X*#VbazSV}ohtNV5Mc^}4RrY3=kw$&3_w&<03u;oNG-93F#q4~$g zJ~xx?NvpC{0@@fJSCpJpI=p=4mB4MAlf3*aXG_&`ee-EEwOZ0*dF9j0%6`d7x3*fV zwyk(`u0H4be*czNLQDs|eHNXtTfK~v`Da?JjKmogeUtshY#zDQd)?`(JhXpU+jD6S4pO)Grft9v_&z_g=1q!S6%oz0xP7na zllA$(|0~V^tWRG*Ir#YJZ$F>DJN$J@`)aYyl|Ss+*Ln&(uw8jZx$?BtSEGWg;y0<~ zb02?wczOBp$4?92g;mv5{r+%S-_Ew)dY=5XLQ~^_hgK`YPdvJj60oG*CXBzo^blu0 ztHxGYz5aIn{9k|HWZG2x`txsl|Ni>u`dhK}^Zvx9EU~zfn(D9d%b@l7UxAbLKEIkn zUwC<$S4fq9SZ1ZSvhU}H%c8p1G^X*y%2xQ8o!oFT?ZJU>&``>r z=^&70?abxyy#0ZQvfv~|$wf+hh7;KY444!Yyc!u>&N>P#I5mY~MujjDtp$p63d%~q|E?|H zH+A{!J^UAw_m%(r`1Wi2`tRWZA605|wn_D*xwOuG8)CdNI8Su>ob7)<-~ISA?)vZR zrgfFCiqY1|9-rE z_bcFA`{}oz{(L>Y?OMUk_viC38P@-Ezd!$3@WpFWShW-aFWo+9c(H0tuB+`Sxdon~ zyXU{-*UxV+>_7SQ_UETxTMPexeq8nMOXi=inf3Ko<+tH6R{$_=IVBngh z+x1V^#s0Rq@pt!s!|>S2*UO?Z7fL@)JfEmidG*QDk4XnjLReRrE@Bf9(RACzBj{s# zrSdhK)k*OmlXoSS{JpfiVM%vk&Ypc!4jbt^OSF8R;;D5(kkyfefs6H$U=sriPm)PP z!$AhdCKHARhL}3JDKd_3YOed{don1#nEmAM)yoWrKW5d1ugqXzO}}@*{a4!k-_y&v zU9MSmu^*ZIN%kY3fPov&%wxryYbL(`|Nlt&jOkx~J-zgOhuxpz5nH}k1U^kuB(i_zU!ma&Eoz4H{F)|_4e(LyK86s zd~B)LAH6?Vhhua1p@M@}3<->lsj7OvPk%oAeg3=swMM$jpMU*%efsp{-!JdkFPpDF zfB*db`&Lx#*;8^mH-1~HTROYGuIDWtr5(pkxJm_0tKd5!`18l7e}`WRp8S6N^Yrch zpZ{7`t^Hgzcly+;Z)BPN)7AUkef;#J4{#{B%~g1xEy~o_W0$`S>5mNYZfgy zcc!(7FYNoVi<6|(qt^#d^Vt#edsiHjvR|M>R8r#onjhNj?;Cr!?)dZIlaWM&{C}ZS z-#$p>wY=eQKdZp(dh%duqe$e<(6_H1KK@r%Um4&2c~{)?)4SH4t$Y|f{WtsmyZxVc z%(si%S9c@p)wGGcmt!s^bhU-NO6ppr^X*f{uh%zs@BjDd<sS)4PRrm#B!&n5?$7+4kRlj)!y1+ot95%?eRs@;Z9R%dY$~+x-8p zK3hk6ulmF2)+os#bRg!QFXxTCPB+)ybb?2Ih@q5t$Dg*+v8_Nci#6sZ_l%zee7%Amr2ZS(_AWbW$sS< z%U%B~^QrLZRX>}rm!JPW^R`u@u~1Um_0~zXk9YKQDre4iKku2d;YuJAgAc>*-Mh`d zi}%SJ{rj=;=$K{r|RPL zU+>wlTj#S((45!a^**8erTeA%<#Te+Jv_=LymCw0wMk5zswekWiagHHeExm<_4V`5 zzdyfLoKI(IK(URzUFq$zJNMmHQOE!eemnedh)mZAcgj_b}D6<aU3_!P;oXt3BC%WLr#;O7e|q`;`Wm~l(#~p%GMhZiCR=X(}9FIRPLWuD-cB+2k{#lDF)_IA(S=G}NDU>CZVC%#b4ms5C3;{3OdA52KR zvgug1&3@bKl}D~^^19P0T3l#Ve7NBHcm2(qnaQ*J+`2U}3_2TaSx$wYKIg96% zuh!!o`oE_)ZSFq3u%@EwSCqx_zjk-{@BY5j|M_ryz1+XAobhq;@2^*_E!01y_UP`( zNoV9rUc7KS?U;4%S72oAZrS`|$PGH>!h7xRsVi+8z)r?|=ZoT**e)9z7`!!)yg&ftHX8`me)Lce!u7RZHxVvcYWT;FD^fCO>(^4zJF26 z`Om$78c`C^y`(5pYL3w?K3Cm!lSNj-oCW-ithDqPam7zb>F9RSoO8x_2b|E z?aY_*b!MKJ5yZi=a3#a!n}NpC6>si3U-0B(i`*`_$z=WJ`Tmu4wsxhmfe)8lUD^L3 zrOjhSZ{Df$*ob#4-3xPKV(y-QcIZt1=W}way;EZ!|1|V3Y%MUk61K-gWZtEH?SX35 zw?6%;_<8$rd;B$i`B$rBZMV27uT?vttt6kl>#_cK?Y@qMSFXAyX7eS@(o}Lu)i0Yl zNqzIV!v}+IcPqMdbVp3SwEOaXuE_~+*>?xM3^@^1IWzR=(huPq9DbdN2=)&4j;@{* zw(ooM1A}Q=?G6dy60Na;OfE`XCtp|oIr`cD-rD`IzrJm+%v(JF{p&A3n`Q6&U7xS( zl<3f3y~x?5`TDl~-_z%mUO4O+-(Pk8kKSwj|4-Fd2iiY>*?)ch{CKhb->$P{?O7K7 z>k-rSQ(X;?>27M03TpY!yC>iNwd3ylzo&f<{5pO5<)_j+dJgN(e_y`;_oqLPUx&UA zQVyQH`i_T_kS>Q7`u=`Ad3dwDz486E>`$+xWBh`D^RZ&wJH)CoX&vEFRLy*fNEsan6yV1HkJU>bINO}s`+D>7=XOsY zU-8xqujmX8?_qvCAwY=Pwe*Wd)A|dyG%oz(h{*N-_~%6UWlPCjKk5n1C>-R0aFvMEjE-v6KL`JDB_H8uz{ZJ58J_v_)RU-RevKWT3N z@vpk=9KL<`e*OG#_}4R&dWNYlxgrX`EvxRj?tHv?Yn0-Pb?NJ#bE~(1Q#?O8cIoGT z-`7u{e?9k|?jkqVFqzfIAL-l=U!I`EGjS_}%XXCm(tCK$<@;~U$)A7!>hD*tl;b=e z?|DCe{`2Xju{*cMP7!Ii#B$--nQ6wwY%VgfGlC2R+M?d=oN`sl)b8wtIg5+Fl~yq> z(&2VxeYAut*y_<)E-e{0nMpE&hop?JeS90<)_vUn=G-o`S*LUV{`If5vN>BRqS~Hm zx@WtHcHpiDVk!n(8W~vm4@ESs-Z0U2d!cp79a&5J+WLz{Rk`7Dv$!~Ja2-16#L6*A zz?xm%bIL2fJC3^iMcyZ7?>2k7Mq!yrflTn)qmf~ETx_KB_8RGhi!@QYJZ*ZuOc{=3)i&fbb0Gx~hDPHb~w zG1k=NcmBmT5g6vi8C65SkG1Rox-Im9r#baGPfn*0S5g^yZq&R)G#%OPNU zmEQ}I^i|F3_k=c>oGnoLvAllAk(Q-J&SqvqL?_a6OC zP88;?Kl5+?-h1DxzBgJv-7(?X)q6&7zMHJ%-qz5*V6ozLpM!gsOsL4)+Y)rh^8E9y z_wWCEx^}nxy}H`j|Mwgf6BPAPkYd@q1eIG^fW34U*1pLObK z#qX+n@$dTmm(^z6KC8P=P~e!mu#5GrE4*hmHTARRE$P$#a(1V$f=2N3jd63G*H(Tj z+U9rl(6tjX6M|k(_3y4g{1)3%s~kE%vKu-un8Y zz4gDY%nw08CrUKJ4``mB$vs7ERrKS#leWC+-~TU+Rd5Y6N5q$a-2t~Qoz9nk zGv9%QW2@Qs56$MAoewV&?DWxl9IVUG91@V)?_D++PLb zyNoUsR|GLTy^g9*D-ZgXta;q9%4Na4z-8z5bZ-%2 zW$EhYm*_BQc5PT3+2oTRJ||go_1{DCg(^&3#m)0hZ50tXqY#m_Ns-l~AzMXtvqtPz zjqb=7-a%UpQ)DID1$@X>NuS) zTw^fY(&mt%(_nk-+JhbW6~87*-cFdM*KWW(VSh!%rwiF4%n`<2ZO@KBSec?MR3##z zk;x{&psJx}F?SJ%=&8wUPHMtl!ik3j7M*7o6z7l>T~Hd%;^(X+!dgVe zp%tlqGtOL->+#Bto&H%xK=zb~NLgZoMvTPwX9k();!GbqpYo76>TI83vc!OW$@+hu-={GPX)N3%r2BkH zN}5bhUrTC2U#KE0gQxGO6T6F=HW*mx+29SbG8%TB(W&tx0x} zIQeqQozqcEe^*HE6(9{I&{xMRv!(=2qo|6K zg42PiitR~{7JfQ!WBb8-_WVxwSJvA&eYPf8T;3gc>Rs{L>Jl#NY_lK_1=mx(bsP>| z3!G0%vd!?=;-IAYDN#T-^%F;flS6~{^wPHujqUr>L^db}nB~qj+Oht$Y)jVO1nGYn z7907(6dHuW_sXtRb6_yKS~+2h6PqbhqY8)3M}@c#w*PwjEJK8nQ&`$mEspe+CHuc9 zUo~-_Y)e?6X3Rn%S%%Jur+Y zhP9rW=4Y0PFdHme)#KE3vAn7IrV@*YEZYT@DJFA`9@xuuM<#x1yt4dsq!&lf%#&+E z&7Ih_eI+8;->Oba^R75+v-k{SwYTbveczAoy&B!S(5PmHf|5e$L5)?%v|@z6@pFi* zs9|tWP&^>n8QqXtcz8wNo3B3?f0_G8SInYAS5VVu$*~R=H1+PpM#Q}!=j#J^~Ia=QQC=mmbexHnd`n;I?DyT2r*|3ztVTxD!^#}utJzqMW0S*QrY>7x(F`Tyk( zWzK_?OH+)EcjZq17SO>kVTx;w=LD9*7zIzYOw|Pb!jy)ZNi6Fobo72)a(YYNo_Ch* z!mUO^2V|wJ?|mtFIyH({ymHIdxZ8FQ4=RW*?VY9bYKtl7>yd_M|mOLeQcQ8A!PjI~>Jk#RnbPl&QEc5gC*X?Ii54b*a z#*0?9w3a{zf2|w?%hf&V9&6?;T_IWE73pMLWudexieZU}3e#ehb~ksPf2o_8Rc06_ zh^TdlEL(dzrq=p;QkMAHSjhv&F5b8}HQ-gNvg-xy!&BB+M7gSQcyC{}JT%~->-l$b z(Vqj86uVbt35qCtWn`t~M;LEk<+_qZh+{&1{qfYCvJ*@SN(Y=oceSq9oVQlqkh3YF znP+9;!)pvqMoU=RLJv&1bvOA-(WXqR$B(0SmOgG0$TJV*c04d6;6lJ66CR(U$_ zY=v3Iu1nrs*Sg%n5m$Ogn8kdfyFj))r)})pUUy|JF_*(pMh6e<`uSZ)?$6Ryp1t=+jk$SzSN2!uIh}7mdUsw)u6lR=PLyx|io)(?6D>2{%$;4QUf;WDjq5DY zl|KWo|IpK@e)4E$QpD1CJJlbxp4EEs`kX5N&7Ri+t&a-Mt%zP;IUy$4@Qm%<^QQN% zPjy-SG2lqWyTn@C3D2&b;4xe?am|yZrW>CxwBha!-f*z!``j50%hYXW7%$&?af-U$ z@rUf2>NkE9kZFx+Ivc|vxMEY=BGF4?%~>h3D{a|SGIcS~9V3rsgotB(uDS4waFgK1x^LE+a0z|DOoWU{BGr|7~fuWhVkR)3x45>($QAX{g~`cB;2%;re{|1LKXHAjI2 zhwH(QCamPwH&Z?!8GirI!~Xi8)+a9nexEnN^Q+3NH*BryMONzJy}SGNoMwF3btn72 zomT#1wTd?z*J;ORmE5>^hHFWa-QM@t`0me{m3g=1>;zfmzB^I(zuxvc*Tr=9!|?YI$z? z&DH1DM;@;YHs6%>a_65_K{>Zxs4UQx4moMH`sCD^N7*+m6MNyF@=!`gN%!ZKPkobr zPo28yJkwH1S2_&)|Jd>N$ zmdvyyIy!uIde#-G_M~j5lvbbV-&_VeRF-4BeQddXYc1c^Ov)=7@ekan8=#(z}Xe{Vr&<)gR zF}lLQW?nEqh-o5+%)JlUGCP8LrmCf>=1;CT$kZ34#VfcZt9!+)%PE|^$qi38K61zi zo-<8RPZ;^KVM$D_{{WBQ4u_O!b^Q~ijd>S ztqPv53>wHhj{j*@q0hffR6PnvN7M^5PztnTql-Kh1=R?7d@~Qchj!m8iPR5 zN$LNWN4K-DEGXPF(U6_9DMG#e=kwxk?d$h{&1>b@&d{rNHTV3wH4pWTo!7qE+`XaF zC*bbcy7lvv4+mwo-&)DOCW(bX(`%Mbf0Aw1h0>#Wcb1iWeI}djf1X|L*~xb)xwpkR z7#$Wq55D{CY}@_Lqv3lDqcoNk9AF4;xHRWYjrJPD>oY^|Jm<4Bw%zr4in-=|>n9Tz zay;Wua9}RbP}a1a_xS0jWeyXCii?9xRHZtem}bvW++Dn2%@2csf;BIWN%jUOE^9Rq zTywh6FS;r*rZgha@rRKmr|`qXb@MW9uN_?yt<{&9zAA=W`s(*B1+gg)bdvevm-jKS z>N9K%F1f+Lv!$lvQONF$D_^vPFE{!bUdVX7xT%+Ig}(q(3v<)~O`n@3%O;t4KHqyj z|Ne1>Xoiz2tw-Gt^nUZ2pnV}&#zW&^@Qce=n-^`Du+p2y?doi9m;a(;a`H^31&eqV zPD*iHZDEjN!klx>WYQF)zv}*nuco*^d$e=^C;1vRV=m(@;kqfs3UU&%&Q6vMjmepVHY(`C^pWRkryqvz$UWhQ)rDW^6nEs}LqkzgoqB z$Rolg#awn_MdPB5@>e0ud^;9OaGR|%n!JdU`S214$CxtBWA5`rgEfMrST5XeEJ$nO zn-r9Lyf#lLN^Hti*3jUu241GO6HUalotjp0SjxHgbU1!B6I$KedC+EC#Z$HG`@Li8 z`pu=agM&kp3y-xuo?r8$XQ#S5lgH_chjMO~|2n?!{<+5AdeWlTG<3wyy*nluKO^0; z`1PfQCJY?HTmlP)N{Z*C#_rYJ+mo~F#>2ng=GpV-FSavUF1@}oIEKs3=>*f_lOgO! zja9Tem@E!4G@J5HV7V)jp848*YWUGvfoFwn!=F#TZC1Q6Aw-^+u{9yKg3&s>-)7II zO=a$s@tR#0c`{>Z>bCuPIwBYtRZW7KLOWWQDjw)^xOq!Kgx_?{mn)LyEUKF$&N>P2tXjKLcS22F zlH2d2vGZN7J!U-59l6G*QvS zK>2lMnOaF#^}H@@nK4mP`?N-oie==SwQCR0Y5)9tYwWc$nd83Ku8Qlkw9at4@Ydwg z>xUgvB%j~(>ve7FjB6G1VcDqgp<_wP@s{}(rKTCJUneEu<1kSw z=%UZ+mV_Pb?k9EHzi^twY&4qV(PNpS!LOI3H-EbuOYYjumA8s?N{Ut2C@xsh8~dM0 zz|leRqHC$C|2OvT>hd^)JXJBKvlfw;zU*1|^X%XGf3`gTeBGe%%6A)$|JT3PN7~k1 z`p&HtzxMHTVcvA_&nN0EnypNq9iOu0`rXZszW2w!68+g1zKrYti}ZKJua;%^&p!3J zRqfBmk0)-gufMxb^snQ*n8_d8*WF*c`#Qf(;Lb0lvinu$e!rD_`utyJ`RAtYZo5Aw zyVm&pbliULv+MH7@#lUdt-rUn>Hh1+-EZ!N9S$^}YkdFL_Vek_)MNkm`Cgs0{Pr!z z(7XQ@-FsPBeY3x8zFPf-V*#b&CCv*?m-t10V-Wn3n$sfm|Ne6>F$0F3nNzovRIA8& zbx-epH?MsC-;$pNx#9vF`Iw~x8)Z3c9Ge)V3lvh@ALwm(z_nd%$xC+2^!*cMDi%NA zYs){?@-d^c%Yl@MnjspqUUyzJZNK3oS2AV0-VtW@D{RM%Sr`i#JPT)Xi*_8{Xu_^j zelITMpv(n!h7Bxki;S_{Tv3SXUoM|Cirll-H^c0HC3iT$0qaI zQ|*<(Oqy3~@5!pQXQu9IxO7%`eeeGN?+;|%`?F`l>5locz4T6RIq=jbi)~-T9S)GF$J`ql>d*)D4$cADwT%CBjM0 zKUeg5SwPvwv*RnV3 zZ;!s~S7XC0A+v<(KsysNTkVP`X&ZaxcFHc~FVfJy`swE3J?9H&{=RupgXz~L9?Jp+ zhk%*|r;9C^Mm&%DE`+51MSdhYRvyOp9Ix0zN&|B7GzdV`#ru5*+^ z>fDCU8~Q?@O{;d#N%oL^cgOjNw)L0$H)qtca?HB^`0=IrQeB>NSR2`7d?Y4Jv@!S~ zd7z>32E(0D&W#&&mK|kf5_qsHK`fG`^{mME9ou<@f8V@m=9V&P(}jPp_*YCbVVl%H z|3r~Qixb~-rm|I2EepkbKUS@|bLh?As;r#u^PS9(%KtGm%{KMVOVmu|6L9p=qb z$IPG=_O1SCUuG*;)*pc^yUrw;7cu&(D^?vkFCoH~b0Fr9#GZ2xW?y$Jdt7?v=>k!P z=>c+cyU&=ptqPIhPyKZwAv|r0KwChQjzWs8fwzzn`xy-%uEjF0?V^VlX(k68iC~av z{V_rBa{{+upVLGGg~W}jqL13NUotJ$;qbWhdfjuL|Gob=`2DrKw8SCfV@8a+)6vzR z1=)WcZsLtd?ASNwRuD(4*M>lgRMw?QcI}HEFgGYlx4D@3CGbq!Pfs?4BwWk-Znfo<-e-lVcxAv z_h_Bj6&krXTctWGY(GgpPb$__34yrQ>O2}8}TCN&m-^P?jXtY-(QvPjM--4 zu%$Smp<%ZX=Z>^~&2>-vCO2$juy3BJU@ESiEN0=Z#n8fJ5H97%7Nn}JvFlG=dC4)0 zmzrzN?x~RpG}QX)wmnee0^_#As1^mbQ{f9Ix;Y9QWHWv=@1?~l8x@gzu^fMTGqhA1 z9-a$Rcw@`I$;e%~>2`m?m&2ue|52n-jk^f6u>9$Aqmicb*Vn z65yC6!dBd>!N~E*K#0M`vq3<45)&(fMh{b$z>?rhArVcUGzNx%AmxSY`zALqEaph! z2xzEKQ@`FD{{7#%d|ma*Ua1e6EC&)D&(28EYF@FBmu1E2qSlfFy{D(?vb0!v>19n| zFh~%PU`|lE;PA-6wDHcgi50haLv^l~*~QP>DB<@x^VpQrx-$|w8D#YqH88V?2*f;k z9ktg{R6#y~LISG6O&Yp8g0F46&-dub+3lxR zJ2D8K;o7Nhzbbp_Q6Z6xi$dO89T#8in11ac8>7eT_8lLN*5&74x4HE=$<;wfrKpU- zR@B<;qEP6|sjq5M z*nBxUm|S`z5Xv_WpOIi}hn)F~*NPLQ8m#SuXHzHh45sAic4X@kD~! zk^tMCZ#@K76)X(mP+&Ny5Wui3;e`y_%oha$k1o2pu_+u#Xt44*eZy_?C*RyJQg8J` z8NU?WP0MOn>bbT-&vX5g(3R=MdnUQQbW)r0qR*-0K*^4x)yvPwwC7rj^l4~5sY!5E z<@s)=diBnu3H_IQ`-}Dl@&7)Vc5Ff04G*0TfwJBU>+BwK$8J6nnS3 z+0D~OO5OY8%$BaIe<7sH+RD>3vEB9ggcY+4Z>Bj|`4Y*n0A< z=e>Kr4Ed(pHspKT-8i*DsCSO_lLcGe{yum7)U8cx1Ql9Wc5W`#bX{@m`U%MzH!G>B zPR3H}c4qat>8(Ch@HK%?eow(BhDOc?M~+*cWEZK5DYh^zv5nz6!BD)b?uA_YM-5Af z6{qDkO)%dgPezGBTL$?xF^Bpx4 zmW3+2l4mXv31Dow8PFY<=#*+Z<3MY!&kX;W)8A&kcJDo+v*4mqaJ$2sd24uEeNI}$ zosD1f?8lW{SABNAMDxm~fV{Y%mJK&I-B`ZxF!#ET7rz)xh!D8h8MWinm!liLroEDp z489r8x@8)NW4PB7R;OvxShuX2v`y`H#fD@dhrWu^Pc4s`miGoGZVr~8`tVckRCbQM z*{T7+{`Kt%1x}WOqt}}Y?$F>9Gmv} zNy)3Ox^mVJfe8;5Gi_xk5S9>eg{JLnH z@$;h8T*K2p9ZLISj+Dj7o@2fIqG4Wg&eBEI^-Td>v!^Vc_BSWlrqfaN>O3JG4TIh* zK@5tAnfVOAatKZhQ50Ue;_%udVyotu?%4JAUh?w4ypJ{gxP@Ae1*kGjoe`K(UA~Q# zD8msQ&aS%=8hADM+177Sb7XwFvn;(H$CsDSe!lWn;q8wvLg!e6Z8D4xEO>i*<&@3_3$4RmGb&V;v9FA@ ziBVm}S@h-Ms%ICc&#U*S-2doC0blbr4Y%EiX^WCKc=45NJ}l$Lcv5ju|C7WG?W)_3 z-YYhDYYW|MDo`%rIl1A=QwC4PB_gsRrxtK!^0Yhd-#PF4={Fj>)7oOP=Dmzre9md1 zivwRr>@Azhx0scUixW=?5$NxO^mNLcyDW&wQ-8urL%mU$25+rdReY& z-&r7)ec|ev$_Ty1^}$QRU+Nh-2yeTuX|;1l!i(*~t*2PmDJ^nP$gtk>VdeCIs1=Om zo2NvqKeWYBc55N`r5oXTsd+XRTGtk?tW4nA|7Bv$dy(S~20~0-v5F7k^zA>Nj`y#K zE%|c#*OeU)Ds&Y}s&dWPD)VD5a;iyWEYvP@T+ejNyvovVabe^7&G|iUzbvb-Cg$|- zi&Jk~%dvQ_&?a5?t37rT51lyAo6Pq;l4Z+2zS9!xGP70}Tid=jS+I8%cV|fdK3R{6 zn^%ck(N)NvxYNYFrsVXxEtho9sc{C~$*Nx~d%Jg@|9-_AKXgoeKc2pE{P345Pf2m^ zR_2^YfgNUfPOCX4r=4mMnmYSJ`Wg3*FP1ji;tHi#EdG4m=G>xsH!0_&gNa+fC8f5h z0^O^w%&sndyWsLYrMizlc16f1o}GLB#o@bMKi7P;bD!(TsN~MT(92k^J%xKV%OcmC zXLv8hy!~0V@!XCR8vF(QvQO5^ZtQBWD(gOWh<&MiS&WJT*X~Oz?Xzv2{(P;se_ipm z)Q_QA=eiYFxBl-Lsx6mPpDl9|35=ep!PxwP{WeeF!t9>0Cfzg_9V z@H88dHi^VR?S$tY}BBS^c^9jZ*T>QqDizh68cF*L2Q{J-|0bv>puI!aw5}!>chpEin zIwvma=eF;67u8>}HM+a#_{!ra=BwvFTw5K*b)<#;ya|t! zyKr~KiVtD$maWq;-XCXj_vZ87>ocJJaD*@_M_ZGb?Dbp@(b2+*$40%6977`qj@AKbn1D@!HzI z-734~WsYt8oU=c5(kr7ST3i<%PJQNM`-&s!`m}@V*kVpA#~=1S9on80^FFcr)=z=0 z%3kYEe^2q7b((?O_p0o+-77Uao4E6*@Gf~f^*QU?wL#^|n#X5kbcdb1v99{w zZu4SvVCa~%V&3a7TKSUQ3&Qqy#Dp-gG`iRHvmATX=(lCc0!D@wh81C&N=cWi-o1;m zU6ahXC~Bnx??RD90ZtQ~gm-eMe_y-GbZxYs?|sHdZ(lC!OB=U8IFWF*waq_sr@LCw zE4N>-ZtZrxJn7C(_0H$b(cwtW|m3qChgGUPAlKD&!sNiRQDw`K0) zL>}|Y4UCK>pB_K*+O&Cfefe!4Plun9S#!nT7##9U&Qi%_+ge-nb^5&B-ar3EObcfS zzViFz`*RZ|*Cemyw7WcAO>cMd22tLY9TU&XCi6_csQTJJZtuN!IVTy^gjtrb%$h4- zxoxNHL1){Vb*o=GnN5>i6cl5`uHl#I8J@AfWcO9M*yjq!Z=Y`Qu9N`JZd= zKFj-@cY3+_JagZ6uGlMnj!S)iePLtX8~^*`q8nS>b}E0pSTA48AN|GR>bVyu{vN#j zct@rDr>Y3wr;+FW8tD|BFuQU5AcvyqhDXfV8GWs%>Q0yb>K092xXn)@bKMEUwU0OT z+Pr$XU1qy(x72mDe+gM9KXe&3`gDcAf3-C>c>lwv5#Bc*HwN_zZVy;nS5m&E(C1gE zqTU1BJ6ckWN9L8xb^4mUG_Ujcu9Ald^IpHnHeGYas#_`RVxwPC^zN@#)}J%-zrT5J ze6?;zzoDs4@QH}$dDkQ3AJ6=FXRC&x%*($$|G&!nt3O}bm+?Hn=2q>iYuAmW(*$x- z7x1p@I(td*X85nv17%6?2rSMS@bbj^3R`%3+?UnaTw78iqeDl(d20ST zmB7pAzJ7jtZs*3yjx8FTDvuQRsR(*W?KPH6acZ#*N5c{suPO!>7Msn^-W&}L zMq%mV8<{R{On-9n^#>M)`4$ep7#O+)I9Z+s1l6!Eidpf!)YkCp;~4*Cv#!rFJzeU+ z@rdDN4XbC&1)*gMM-~S4ZSI)8KHJ}TW$LV%*EWh2d6dMI|2DfX@LvAzE&cUZ{i78o zb<8!k&pRy@chO*HjkVuSCFlFb$0~|d_21p9*|RD8wALEMRcp_GD)L&US+XrQK3{C3 zt_VlZUTe>gf`nteukA3>jwGeg$qCaSZaS?qANXPuX4Z7t72bO z!-tM1!m@gO;?{rOD!qNY2o$f zX5aQ4f4jkW&yC81i*+pmOJr}q$|+wuYufUoCax#6PHx-UwTm(HZO-&}pAy_{EiH}w zwroKiyj{;(y`+i|QSzf~FWr#s0z+U!k_g3!X@BTXRt9L}9=uOEIsh zogV{vqRw#{?yEjC<<9T^^w<9TU&sEw=v&WmVT)ZJ%gdi3Z=dr=|5L61`Jt+M#mVO4 z_}}K%|K`uXbw2NpbNAJ&*D^YGU$}qjL9Ed3AJ6LdRAx)QOi_N_6sfRx_3Cj&FdA8W$>IhyH(EXry!?t@S^`4r%rpD zxlZEt!#!VCTs0OyDzjeZTFl0{QsusSrM(ahOvzEW%y7Vs%jevmu=M;svB0yx z1imldDqpL&^t*TY?1Y2jx#fSqovVB$$54ER!Af|#Tgt*N5no2{=U-#(?@i%rJ2ZXv zxx;nE7FJnXvgghJk(ztG(*D!0$bxl?iaDp;eJ;7&CA*tpM$?m~HP!RhJ$Zeu_+Y4Q z+@FH0+u9|vO3t<~h&M8RT^C&~{=OmZd-3XtRy}d5;u~{~S=W`eF6~dX=S{Yoc`$VI zJkk1dhJA|}r~im9wwv^z^PK4mCyrjfyN~5061LUlY*RY?RDsW|OviccwBpk|EXxl{ zTuo%x-{HP?MyUA)fh@OzS&tudU-?k+{tv6%?0ac7Q6DpJKEE0J-Q{TRg=t?aww0Fe zT|I5}WRsBY)U{E5yWF`}^-X-JAQ!iF*4t^8Zno(?Q8`rDr)d7&HcPkwFq zy7orV`ss)7Z|#~JWnA*7pzYJew$saRSJi!*emf#M>TuP;#tCjqB@&);8^v6|@bbXl zyyKQi*wfAdEH!=Y8l+tZ+rP#uebd?dG6Vw*|O}SeD^ z+tTxVgWS%`eAk~ks_@M3`6KbN|InqU3vW!l=aISk6{r2Y!z=c@)()D+uef7PctZQj zEA0PQ->VC`w6)aa+nFoB%D#Rpz3}tp&6%m(>woX>J<%`7n!1K_D#ywW?N^?+ZnkB% zX*lVf)LoW%#a-gCz`V6mTMwLTdU$bdsdVY}baqJA zVwNJmwoMW{T7KR8dH=nQp5>Zbx|LN%@AuX((+}QqI#yqPj;>$s#!ZWSPUP+CS+mr; zA@cT|X@whtR>km_zN$;sNRH1w{bl;WUS_LZ>vzA8t=!S{AT(orZj3osx#N_b)xOGq z_lPDHuAH@Kedv+IGk5LYPP*c${rp*VMPyZ0`utrlqqbc<`(#P3)|+Qjot~xEM}3;3 zb4}RtPf3F8*_EPE>qJgP$UT|KXS>tzoX18t{}fik4+~0TD^1Sy$I17eZ*#vG`TEY+ z$Xwm>4NYqeKg;~yw>3GYPi2E1BU6>au{#X6$|V=?_S+G&^+L>c_ORVNyW-0Hm3@yc zViP@WcfR&j%()b^ymGDH4buWnbQxM2tpD@kg_HQ3J;B?%e?6~}zx4Z3s&2r)f?Ceh zcM+R*_mBaZ@#We{jj?DfL!bOot5Sh>reijx5Ij!ZCdZ0 zf*%%l*YrI9XI=C5ci-VEzq*fK&g!nYT;OHJccy>l@tJ%4lkf5d8LvoR%G*Et^T`|Q zn_hoa$a}1}yX0%tmA~IY@2=Z-yvjxYao^({%VKAGim*(^8uPtTFSTkoFnGCXhFH|5u%m8F^syg1xmu9;aS z>3m{g$(Xt*?iAgY=BuXL`IjeJ?-X(q62PclD!5 zs@LCo*cCta+Bi$m!E~~3U|R1Q*O{vfmi#U0R~B70tvQPQQ$_WI&);^;ySr^y*u~ZF zDtB%!6>Z|k;*5Ec_x5hg>9VF5&o<`WF0nEec<(7|y1Vp&h2H+tzoPq-e|s)B`DOk3 zX@c+90*x}?fa!0ih`Z@8KegtC<=O9g-=95xbpC0~%@5iM4_RIG%H!JOc24~&@=NQS zb(-?)ny^=u2F}b$>!Rg#-oBj~c+))RMxot`K)2H5ZI4T8=PbLo?)|~=+%h(eE3OlG z84MH-C9bOI-!%2F%+j@i_RpG@J~C^Q_#5;#qq<;jrSNQ#JuV`>T}HRJYZMIW zS(;Dx*4?VJGSzJsMPHBnys`fI(+@wKp1=KaqWaSWzC_LSX^~HCpFG&ft|RZIDF4ie ze{)TJocz`ApMSNkEBsyL>oX_oS@!|_4a+knFT-)~ZjI(`^&JcwD`)U{-FZ z$JB_w$K+NY@6cME|IONN{sd*)S;}XNLKp=YjUG4)T=sC&61ud&_-*A%sbxC0)!!2N z@0}9$5pWgQ(9&5W5c5r6u*G!7&acnv|G(dw>R@r>>)9i>x@X>8ps;~&>YEiS=C9iOLVasMKPEZWpdLjPUMT^ht ztE2@CCNJ9Eb1-rx!$Kw&i4M=e_$NVOuh&f4)I5D-af@ zCXwgn-k2_=$~Sk)oXZNzVSPGcBDa&{b*r?4zL|W!eX!!?^Pr#it{YDZ2x9Vj`T5B ztG&Cr(!We7Ds@ex5Z7Xfq)gV9nREMh-+r;JZZ(Vaj~CTRPXug^NN_Bk(G_{${>|1p zhFcr`j|eC%>#e@kU-^A;{N!nNO`I&c*C*Uq6qW3`z}56vlji9>C6UD~+uUY&X(YiS}a#!NClPk5Z?6J7KJ-p0y z!{(^oJd+eDHwMN97I70rcmHhvakp`P$$O9Ov-6DR_I2q$n^wH=>8hOVa-Q?Ef4%Cd zH52KM71p!bwJx)}xIBGv=XX9)`Q<7eMO;^{rZ}E1I;W<8>B(G9$2lqMa(sg?e>7z% zl(h~EvWlFe*FU{>-qQQAysvabr`|Yy@?>USm{OyT0!t~!$_p0-wpUD?UdjJa#Q5G5 z<2}cu_ne(#dEd@%x1Y4#Q*DN$Y7C-p)c^Z`xqIMtOMR=9X7n#grnDXvhgom%(obAoTPZh`ybJ>I4(r0HiY&ySjX_4)$3YpglFWj`A*z8z!?p%6O#o2(_)_e=OazYrmE+ieD zpd%(_z<5xAH|HpimB1R|+X)e&yFE*8ADF|*ZZ3U_Q(Rj+mN_WU>?>2BW2#e&`GzCM z8txe!Y$|o!82EJ0<*jF!=6~Q+ml07+VGwL#d>$NndxB$|#+9!s4i-%+EipM&JNmCF zU0lyRO=-%rAZ0P-E-jt|d?yoMPDuP;aaF=s%{e^oSo|{Ut6vwSruF(r%g#Ha5&29x zg!ilScZ2vl2QeSkJk~#~Qd~DJrg+77*K#9 zG=+vsvaDV08h*OWie0yK=|xRHLyx61B^?wrT}3<9SI@asNl>a@jL?+N7&~&oXSi<@{9?yHe(^lTS3OKYm=l^UUqlR=;o9pRWF! zf9>`1FLf{K&wbx_f6bAiDKk_|pMBZ!{hQ9dg};^`Ka{2a_rc%&>pQ>hU0*G+{K>TC zv$8Lpmin9ZKW+P_fA8x`>g}ps&-%aivtGR~>8kNU9hKb0o1QH=BPsdrvQ}(-e11r% zeM8*RiPPFX#i{2~~ej560$@XW3t5hq~5SpU0RCD75f!S%9qFjPL(;h!d+A?ALrv0_NNvk$; ztz}uLH1o8uLV(uF3sZP=R%mI4oKuo+30-t1@hOLjaY^%rjrp z%34WoW&BW zx7_@0({8btETeDJ<+D6@dKwt5l~(iLp_?eO{rB9=*uR_C|NHhYdB?9mD`ww5QM7RG zQ){#Dfyd?*ukG9|G;vX0{YC}1kX5CuTpahtZ^EU!o0NYT3O|(dT2=MoZshs!{cY3c&A+rf`(31JX}ZmqJG^sTJj~Z# z{qka^>4l;TxhGxMtZ0khy*@H6_FHrBVtf6N0>;3I11t}+b{1SQ2;QuCQS)d+NvM5w zxz)B+Vz#fu4K}?JDKWS{X{HggYM#f9M6un=E!Hzjerl3=y=jYLWrws>l7*z{Ug^^U zZjvd&=eQyl32n5|f3f0SXVi)~V;{$ky!DA$)mPY5J-*ubaHK9>yy1^iLekEV{JTL; zvqD;hSdTRJ?vzkB_D&DGHDQGdTZZORk2Ym?4 z>Mswo+=wjht7)(GBwaddwg$c7im1x=k`6tp?xg&lTSae$;1tREiI;!fF4_`x*8IRb zv!eXCJ2LaT@>a%lHHB~3b>;$Bu4eLyZO<7+nhYdt7hh*P8|Nu{Kdblk#jlQ!IMXiZ zc3Ca^d~(VSX5qeRDp^WP8`)UcA`TpMn0%*m9@pBiU7Z|PT)!$l|N1=s+D+C!E?bzk zxGHDr-g@5=A#-G^+J~OqUv|yrTC~B)h54}u_p}*P1CK2fRnfa@B$8wh^m9+qnL9Sy z1kHoG-YU9p^I9Bje)6BClNa~WFf}FXEE$okX$%z$rX64uKH7Ta(t!nK5f0Xk3=N4# z=J3ru&AW?HVVXrtAFGrk;|dLBqq(z|ub805rfkY0oAF`;PX?p-p2FTaOG86gG@CE> zzR5UtuVg{QG364a?&~YKmNK~{+_h<{JmP9-K2hyn;tHiTTNWrNDT!yV$gf}#;&yz0 zr-ISPbko}G*00eEn}tm|PA-8>}H&(G*Y~1Gf=~$K)<~?W&>{`0YQtAACnZ%{mirvYAH`SVs zs68*B}c_Zh!z^JY}S);eLC%>nQ@T3>jf1_c16`qUpy?=GU&Wu@XU8j zsK{!5rMP<2gg_3aB-e|F?){zSq-gaiGP6wSj@!#fFB6u?xQxr03?h|}IyI^oOpb7A zWqe{{;$pz@&@EwA%MvctJs0}jJTePJ zk6Y*26>d;*-^3^ow#RGhFC(FaQ~oEJu`ws~v~-whu*_PL%pfr-$*IL#&0%tipaW}0 z@HDRAhHH{eA>k5%oRfVHND8=kD=7A@;8Bcf(_mpSIqA^B#K3W=VMgGC#3_CkR+yUV zh+ew3)`TZOh)IA;O}1qw0~bRelR={a6N5|35e1gU6GDP~6AlRk9 zzh0Wve_pJaw|#3LBcsx%lWnNj1wJMW0YEQzkKRS{yA$qTpy zcrP`n2rNE)V?+Kj)#Q|I+d|e%eD^Cd?17WBl;V+=AaU;GEnckYCr)%s7F0c!EX+Dx z*vP{Ef#&HY?Z5I4#4c^9K4*V_!6}V`j=%)>DM4HUL7BXXVW+b=j5d2Kx(f+J_8xQE z%2Zr5p~EGNCGGSy&r31f$tr2ztKGi-$`)D8*88MuuE>;`?_@1g5{$0B$eeLD+*Gw- zLi_1Jfkdv}#S=V@w$F`h{-RPLm$^Ya4GYhNRK_)Xj zh5`4Wwxa`N3ZaC$**>~H>+ILETso+GDbN@oq1-E}_snmJwa$`CiJs=PS;$uO*US~n zk7jD9W!a{-uTYDb?G?5vRU*x6g4;_k(=)#-Ls}>Erj*Q2+Enmn!rs48MoVwJcYdOH zeGmhwm31)h~RW-Hq=)8u%sheX64bKIlWO2 zd}KJ8SzAhZ8U<7f*n+|?=y(@(t<09-6yF^c!2M#yI$29aqt%B(ZKpMQO^x)Z>W(yf zpmKmIp(llT!bu<7*PBB$4vGnRhaTaU+~GFc;z)yoo-Nx9w$7{P!f*AQYcOnUbo8uoq+0I5(A8PXxNIh_`KOh} z$5fQNYtBwxuY>?`x5YfVDm%+J&5%#4+0wBK5y;Uu*5B zMJ9AGZ8ciPlcU$XP%i7vRNKu5U!HZ#R#~<_BEFXACr@#E&Wrk;OI8JRH7=jycD3W} z!(y9B$D&wP&UcbAS=JSyvh|>xl8d-n6M5_|y{-*`(z0k7ytoVgvYoEqEjw;FCdAt3*nYgs( zrzsWR{%pFn|Jx6xIX2Ja+&&6ibPp1^6GbqO*gJFMNM*@#NRaUY}uW8+Ya~tEwcD_a#G&vKqukA zi<@6hth#@_VoHwe-ou}Mtk*A>U!K@o^X>Kf`iYktJw1+;o>^TKzT>M~#`52XR3}93 z6V%J#jrOWM8r*O%M=C)o49q) zM;}kK`Kj~f+4U;B3RTi%ljgwVo_Yl2mkl6cE(SIYChMG?Vo*7A2w8ol!TR7qh3hG(7M9Yy19Z z1=rsTslT3X`riBaR=m=~=qcK-LTzfdCpg*WsmVXL<(K;S0;wc|9t)JC;j!>#dnU^MMv-cB6T_kX|Tt^a&aFHPi@hw_~tD_)%8S{lK_Xf?s#{D4Ie zZ$(4RgN|i?6n1@Fz1QHD!TOJ1xBqzF*UtXecYEJmtLGB$%7j7{Td$>gWxqT7*Xro? zs=B#9et)^8TUUMQ{*{k6V|T}OKHSglXPKM5VnX6Yx8B&cgIOs;sk@))dKfsaO8TYF z`*xA6uBD=k(rO05iQ(;2gmhOgI z_88X}%=1ocTfg>U(U}wL6|KA_7e?KoT71^{R+TW4Wf61Jl74uzq^e(I` zC=j?cYR{u(u z-yb;bo`v%X;cYQrydIu;_JXs%LB8kvoQ?nY)&F_>_O}0Z`9H7E+dJl(6=*Io=-oO| zh+~F}cm$*3ECmToCeCFm_q%_g_z9-<@!-+zZCzBLN0eHCmeE1CE%t%tvYIol?n@YB^v+@?|r&pfZz z)Gkq0SQ=0+buL(=@ZhU=-%WD7HCtw{%88R`5;?^Gh;6~v8Jxe~ygPF7$)Y9;hqqPl zUL{O0ZmE2*<+SrNmUFiJj;a?K{gynxw5TVPQ*utJn*Cy7+q`xe@7U>n{x8dprB3y& zyZ5E0pgMtjRb*3lMS%Uo!U^(QeZR@CTK}!$@9Mbvy1Kvp|KI0-uYYqleqDG`reBvr z^u$+pf6SVxeRaa(k5d%Vt%Kv0nCHHfQJfXwuzCBjt#jn{t^Q5_{O|t%i~9TP?PRxa z54nH#yms8`yZ_c!Y(0GHyWzXH>f3!5>D-(5L3>BWK98`AtDe>Dw)^+$U)14f|IeSV z|4UJy|K)4(Z_#Xq17aSJrzn|qH%2ec33WTx)mSWO93X9d?nU-3p8S7*Zm-w>|3AO~ z_w(8He>L~&|6aUI>ZOeEuDChvs+!?Z&C{l}WcpcJ6>y(y`zsZ5dQM@yz1{iz`D?HI zINZPMw6k2f=wGX{%8BP=rfv{OZggBmUn1|KVEzEaJ;_e*T3V3Emk_lXZx%+TFVr<#EU<>fKz(0*q37&j1f7# zO~Y4vLrgmQc_Rrt1 z|L%KIQF$i0_-ete9lO@L_AZ%b>hBozDW9=)TlnEiQd@4#ZFSQ(&sz6J$4|2_v0>3% zp#~=Y`I&o;kz zQ$?;tNKCS`eYX1Jg+6w9?fE?RmF}f{rmspce74UpZ>G(z+vHiZ9+X4)&yMNtUgUg^|S+{c-rh2+^Zh; zZhfrX@%Qcd|2g*Z<@@U;-aWs2=J9>670b=<@x~@Hp6Dxmv&PCW>QaO2j1Eo-Fc>%kTfQw0>p%?lT|#ti2boUFAEZsI2T^kjr}W!I4=89jhw# z+^%1`;`SfS%-R)|U!q?6Uv<@BXOP_`(XcI`xlvL|=ctXYfAXI9rhC^NpPFm6E_dR( z{L|as>Rfy17^D|e^u)H#{kGY6HoY|o&e?0SpNp1yzOzY7Hsn|u!R{n+uiQH+=VeY( zYuoPk7BB2xcR2LME@@uNu(=ZU^FJ^Bw}0{f^>hCJD60H$@fQ1>`bXas{uv*WJZ57o$pKa=H{ zY-x}sAbHEFZ%sg&z{|q84_vRevi(2z|MUO2Uz=@W_Sv6`Z!0@2zx6FsgW?O-!;zaB zA{+#~CT49sBQQyaC(G>?XU+?G?FzO!+n>#EE#mIkeXp>Iy%(*|vCxZYBCpa(9$qb( zjHGKbdS7ihds+Vf*MFbm`Tx9l{?M?fUV8mGV@vFpkuB$y^^4ebAb=}nO z^6#$Of8KwTc=h9WR95xE|1*EF{qI$7DPc?q$YN}kR;g%YVsVR5*q9-fr}W@vq3HGg zn*VoV{`;SKU3KmM&&$W_pZfp%@S%iRX~o|?yCxJ#@73M-?1gN{mDqQx%&odt4g@zB zRkuI7|9}7g@Bd%^U2GS!KmKXH%f7?^I4VTcB$`Z?C9VqE=dehK$>Nlg=&tP|mzFZ@ z=#<{Z!TzK9EBop5zWcwMef$0=FZ|w{KMM``3g3pLX7liQHZ-Iu9=`ZtrvA;i`1}8V z#+%&RUHYSF!|bmm&;NdZr2S2#G2qR_sSd`yDgp;ojszUpF+oi%g@<>8XUe?vw+Ftz zsJ{B?+t0W1|2}`S{};R6RypkR#U!r0X)e+?duAmt_&r(TyJgSZ|4&af*1tRWUP8M* zFm|?QgVO?)iQ+TYOmx`BU{P39l2MzTRu;JH!` z(V@Amhh;;HL#%g(N#t3LwAQrtb+UebD8+ildCnP-Lf#U)4fN0dKJq721yT-SJxBgef|6P=huJJ%D>lX)m7a& zzPR^ZfbrD?uEIZjDPMBu=zU&uE9fmqjc;`|Du$)+`u^UZp@yv#%46UZASu3@a zrZw(MIlka1TX#v@1c#7=PYSBcjN{LJ-1$SQdTZYAuY2YEHsAL!ZQgt}Yv!`8ai=X~ z-`?1{YN5@hb(Q~K)yh;o&;RiHepmkU*JpMwlJ*r?c;`#3ma=SBedKYQG(p}Ki4(QB z7Oi}##QJK5%oTm9+@7DglP%lchx_$>(`k`tuUNA~QZ#LWjuG48O=UmpZ~dzh|8H>X z@%O^T-~XRZ5!JQ6SvX-+8e_8D0aH!U10%_bt6u=%Ioc`(k zudeY&q@VoluGg~CA}hOa^Qk?t{`}WJCs&m8-@d%v|IUo_ouV=<$22Q{{y2Dn3g$Or2$=+gJN%QeE=dLvv$4v#i=>6}v6=(s7^K$6u8{pWP#W z@6q*#OP*hC{S*+kvxhQ`}_X zso(7^y!_o~1vs%_l??=eI`}x8)?~mBR=NnV^ zpD7N_Uzy|k`1nI$xov`i{da5-rdq~ihZUj<8m#_n_Wp<~PWFAD zwSVnj?#t3vZ#?hBI!`Zpx|xZobfv3m<(j|0?8~3W^Z)px9mhQHfA}iy`HV6u8nt0- zR3~km%+?{1sN}kK#WGWo3l)>Sve}QX+WMm0vu6LC`qskN*XM6}ZEn5q>&iE~pRDV; zsMW6J6PNvG$COHw+|yrW^jU)6KU{V9(MP{!dLM%pge+XOOi1C|oTce=_oh@#y!3Y~ zuiYM>zh3jbZ8z4ft*+0Vw^gt5l*z>3yHEFhI=;Pea(=wg*6whr=P74w<-R@(Yxgc& zWu%w1KJsno)_rRG*JR$VuH8QC{G1r3ObG@q0ddnWt2E0$-}^SHGN);iN#SqctDWovgu z^Ifd?aQ|Q3-|)xrkE(9I+_d+9Y>a)!`-QF97q>omx;ewU$~fQZ3ty*pqRXRAFSh&q zn-m;ps(b#&x&Qw^O&9)mM7{9czg_Xi{{0ELv;AW2)g2M6ukWXe94*+A?B&^;IAejK z$=|KA%T>jc&)?rEcZqRXN%XfX%VsPwIT$rXWR3)n97m8QOOOY{l{_|)xKn~BOuwJJ zv-F#V+PYN#v+Ez8`da#BYp=x4x0=zD*rw&`dRS}`Kb#skR|7yGZ z-uekX8yHxgE%SUIlx2KCdX>V3#4H9@6VL8MrYW2rE$-Xj+zxwr;m^7G|0eCuzI!k7 zy;jQV*}s2Xv0>5dh&ZbB`M1=Y%US=9&b{0Ij^D!TN1(&bo#}@!%sHU_UOTN{LM>p% z2~Vl3Hv%qY>1}yZ)Ou*Q*Q}Mx-&a*VUtX5iw|TYvyg#Mka?ht(zBBO?3@rX$C@Cji zmi6rC&$~w3E)^{6_MiOkp^#p6nCFUyhCedZeY`T7td`+dCk4H4IDgq|_SYM!ljOg! zzy5XE!Q8AXznJ1FwsLm8%6>&IZFwIox`HN|j{uz9I;OTs7miUyrVNY+|ebzPY+=o-S?_+b` zUwM4`_T5dYp?{C=HE|0TK3g8Pd&^qg+SHirs--IneKo5?e)@!HMn2wM^!(I~0Na=k zC+@9(%Q}%~iSyaX$8!YOBCiQ9T*Ds5!O(f+pxL`L8~-1lF1=s+*8k(BbNhF0oRY>@ zs2d=DE1tbB_Fu&1+u!%^UHAVZf7IE1bJtIwpY^-@q2SyZ&Kg%7bYE>-{r}0V|DU4z zXS2VkpOD>Oq#N?xdj4hqTT{Qy@Bcktjy>Q(&+{E+GxCCW?yFwQ{^R$<83(OwbRBx? zxxcD2NcjXy9$j<8=FYKq&myK)C;b0i9DiD4iw?_n)`R{UYXzoDI2dS&oc*8n_nG>= z&XiRgl8YxeDExCw`MtlrINaz`L2t}jz4^wD|0;h5{wTc?{n0EhFhjU8G+M<(9P zKIAZ~t1D=N(1!l0D$kyset+_%{r}Q(@zX1>?|t_B+TEu@XN@|V)~>%)e0qQ6)t_H? z=v$oMIgcl70_Thu>}NTbPncV6v@K@JQ}=ahweQ;r^a;6 zD{U`&dCR=;w?L_;#iaPtZznNXFMnRXTtEM<`RgD1`sJ(Z=eUKIEb9&3rP8$`?3BDk zyyTVaO^q9^D%ooi*BIBN8a)+L*6(k6wRPz}sdc9WB)wv*zn!^TtF~C;*xt;=XXelO ze^vJ8w6#hM6JEMBWChJxal2>pUbV;R@-?x|+e58{PB}78aDVQ1zntk) z_ARxH|54R*OxgeX;$>fd>eff?Ew8@!I@b2ulbi1MdZT~6Q}(|%XKm!ao%w#BpI%E- z>~)`ddBvff&#(Ux^?GKS8fCm~-R+EX&A*;44i(Z{;83!J^T(O6Q%u5X0yCR@l9+Bs zF0t$o{j=tl)xD$l_a?hD8Z1q!xWkrf8UOBS`}42rwePb}zn=Gh+VPFsy!ZZ#t?v2Y zp3oa&=CF|K*T<`W&R5-?ZMHhGYW?lkn{Cea|J?m;Z-vGeo_Ob<7u4QltnFK<(RXL7 z@7s?S`(%FYoM~V8_203t6Q0yR_<7u6<%?6_HvHVuySQiZ{<(4Aip~Gc+ps#f-FI8o zRvx1p3RAbdaN}0b{#Ury{>#pdcX*R^n{+0gI>`J;|Dh5OYsry}Y~!S3=Pstc=s(=& zRwHxxdd25HUhT?bcQcFcc)9=GU;pm*f0^?4uM_X`uYddd_U66k)@219dUA5Z#I1|% zm&bN*6cn|&=s8uz`TJDG-ieVST*02RS|%)*lHE|Z=JO-oe;@y!FrD|Wh5x?J{WPuL zMgBM0wHC~X=xGTzo4X!D;)#ggPdF?%G zOPid~(z>#%Gk5Zb&v9M!sqW-sU;jg&U$nc|-rf2=HZT6$+t)wWXn$NBF<0Jl-?xvp zoX=8@O>kjzU)UwIN+5wJ+kC?juKc?}g|;`OYgBe+Ju~4pnVglBda_Avs#2j$g7%rM z{YQgDHoy9``fujfGk-co_v@_J(PU&5aMDn0+2Ho=%{S@Q)qi|`Y)gAneOa_rvQ@BP zmj1;R*Mw!O-NHUi|BVOz&EK+%5mtJF@!>9=2-ax^=cr-GA(d{fXPF;urk$x&Qn4f1CeC@22zr7n{4j zwEvd-@#*#7vj02%U;qE@nfTfJinCt5JUG{RioV3*kVCemt(X2Eo4>B^XY|iM(YJSQ zne+H4|Mbd?PoIO{8Sj_RPftA;Q6HRLAo8W@K#r;u<1vwA89j@lY#q$P|NH-X`2Wdr zk=Ak!#zXgyME;BEnd-)wFeRzWrS;hc??BJ>I`XVfMZVNNS05RDk=DB(R&BPd-t=zm)U6xeUfrAZWZh>?O}#l0 zQ(1UJji!_^YJT%v`hM*+^St#RuGL5{&p-ZBD`?x9nike;KJ$0$Z~gZC@Hb^^`y*3a zo0OQ1ub29#S#ZZ`Uv#;@Z`IED`)iYOr(RliYhV8IuniLu5{@u=v9y(a%FSTqWPiEw z`eMuO_^f)x&7U7{th;JgUz}9qf9m6}hqqHJe7>2j4?I6niG`CzMQO%yqd?(R9Sq7z z4)>pjoIS-NS9m1AnlWhMvQyr(tGc(=6+O1HDRn!LToYmN@rtCJ`l)jaGSj@ue?OaV z^0xBVyHkQI_q3nX(NJ7;`qrP;%=W*}*2)%pFfKe*r*mG|*NfXL+9v(e3%&GHD$koN zp311tJfzv^buI0uN%S1mUB~!~Bd!{FaIWW^&f0z0kVR$9hEuOCD@*Ic7f!vi(Oxuu zyTgpb&5Vv081rtVG|7cja5**@7#Ba<*>&CG5yK9K2^&KnZTk4n*NSVa{J$G>U;p}e z`R~8eS?3y`URv`s=)XnyjXxXou5aHz=NHraY1&>(Z-}?XIH|5K{XX~Wq|$pYPR^Hm z^zYo}cKs{2Vl5u;n^iq0{^7&xdv~7SUcO4Iq@AJRQcS?vXEmzxF0Xt1^#1>^PtPCw znsjl`nsuRbXZqcHUuoO!>sr1(?`Qw+r`%^XSXo1w8BVF>IwmtRdUH5OZoOYo_fROK zPcnb;L>uSQGQUMB5eq~wFr2+8Wwv4Nm$e@rReqA-_T~k~ic|1F3`t0n9)pf%AKQ}#?=c3nHdY1K zBAwGaza8lde{`|^U&o?DL2joKub67RHJVqnY@Vy@mdZG0-j}-`yK+`$#9b^EGVU6wuzky^^K@VNFS@pr$^e6c;gE7Wv>SJWPtM0?iEMUEvqzQ!~d z%t+xp!;*F=ASsQ>;{=Za7lT2E-qs^hF!yze` zgx;rlIvR3Uwl}a=&zZZ_b)EB-j(1;9U2R$syIXGk=2iFazx%t?`LkBMxe_vwTqi?J3e*N_4`p@HXr8^S^OIkNt=xX|BAKLj#X4jQJ9vKy} zM^_tiuC)k~W7MD?H(pW|ag)2{UfSw#aq0fC=n@Td(N;Sr;1H#l@G181rCGO& z%GYYXKbIGLw^Zt3+}`lKDLZq&=hWHf{;&97u*QGOnc9euH%p@* zuKvOlXwcB5e0Fv9=Jv}G#;UA~11JCfm3P8yJ!j>+^X)&Mz7)4Vx^2-lQ@8hXtfDVR zNt}B=b-no9mip~y{%39H_4)gwqr*$T@Me<6n!?Q2?`N)>CtQ{sYw)L$sbwdRlEQ+9 zBTQl~j7*#g8=RajF-5pKIe3&_3{cDyVDY}mcd$-UwDO})_sQbyufL4^`fcY<%{qVm z+v9@IrPhB_V;?7UJv-&Q{->*~{0xtuZ{DbvYhOQmhPh!)!1R@8*=rWDH!z5@#7vp6 z!HIM7Z^p&%m!67T(jwY=`b%f z_{>OqqQb#?HfZAFL#1X`4Guz{{>3Zq+wkXwh{fD1nO7L58?}mSZPLu^OZ(e%-&|w~ z6l8D`aTO9txb1Ui_Cv{QyoqakZaq}v4GvBf-IX}Sjp5S4o3fG4vKC>}r84KNid>&1 zmHce$&b@qo-lrd0hR*(6R9b&gKHG_%>)X7(*zPN-7lH)6^_GeUneVy3>Hm(>_KT}l zZ<|%z?7n2hT&L-`3YOI?tFPL@t9W$oHvP4_w^wiCTm9sb!AhNXrk^jr<<~Ec3DSFV zEHZTC!>ENN;#;?Vb@Fr4k4<0YyZp$q96=9O@A9`r64O3!xS?QmFQ=zzDub|O}Cd(IW42yJ{y|#C*nsr9j^w)o%$IerT>Y1~vzf|5dHMGQ& zF>>hyjkde)cdz8`G*{h~rM9c{ZV(z0wggwfA4TaYZ)w zv-%6}2Txq};tu}F{Qkbw^Lu9>pZu-=k6n=c4sUfL$7KdP0jC59kLI?6F-JI*9D+SF z9Vf?daq1~LwS+V*Q82hPBau6iHTavG#?~#14zbu8>?XE9edv*KX zJ!|(|?66k68^Qj#{bi-paSQF2AuUfj<-(RE3T&FqJ!b{efgn{)M^T-S2?8Q>Vy;bV zOw1BbXth|QnwoRQidobCKJV9+d*)7d{haV&-Qy#ZE|pG55t6v@B1KG_r$K^S#b`%* zyuxHgXEkP4A-%NCWxge9y6br5r@5|-TE0MetIo-oWOeWE2`&tRE&?KqKhl^@HHzLa z9iAb1cq^-eZ!34UkUC3%(b8YfDmLl;6amE#w3|UJ0T$fm>lt_sj_u0hv zQmmb0D%W4r{Tj&^&hT&q|4jWgKkQ!j^m|)F^Cp;H)4p{m)q#DN$M(D(r7{Z_gm}Bk z&Q1$ngAnnAWrs&)Sv)s?bJU+Nnn(zB1(G-qX1&WCt z9Su_$m}6FKIdjs9li4AmfI-xQWs6)fth`R%_p{JgO~xzhcd@kIk!E2gk-$`@AA&s7yGjI@d*M|E33CyYm4okT(PDv?CF6T#T7a# ziYzK;Y|E1qIoki19KZ5)3DdDCZZWBzg7?Swp8mp} zx=^ov_Z0iQ^S3SjPcsgVdG~bF(XR`Coc^Q!*XHiMD+@&;)N~lQ8XFfFO*y!$=UR)- ztmSv)Ewr5$E}6i-YyFO@uUqB+?0U5&WwXNqr^!9LF1T)(aPR1vO9m=SJp$R5lrz80 zK0EE-cj>C_aXybWM#N;Uzi~?Oo}u%R+$DRf{{5^-J#IhitB!N$wIxz>y~X$<6C9eg zycA*r8eL8rDCKmnSs>Asq|J`rgzh+m<2O7pmbT(0 zQ!KSP4#?E7vKe=)dmS?{PR?XtP-a=kBBH=xp~%I;xn+t#@X;rG1gG$&{W_cR$lLSo zdyL4Fh z>MYik%$ayURO=KA&l-+|6BoL8-(GvUNt0d7_4T&CNt>3-|Mc6xRQZ1UQLS}(f%41T ze?OkHp?UVvS)Z;1yx|eu@%~EZEQ5d*P2PM084NwG@0R%5GTlBT8QobF;aki5^xWx* zg|+PceD#IZ_-K4EN-#zxog3=Cy+&;wP|+rO+nF{yWWSd>xcV9A1pfmCaymF>UzH)O-trSmq@DIdi1H`qkHN7 zAK#_tD>W=mkO)esm6*yh*F>_7`y984(fK{|Y79k%9JX{Bc!$2Td$nmrRGQGa1_q^~ zsjST3Ji6{4>e0xWps-9qh3B8ll3NE3g9=A(J+&|{* z64`II<3waz)Evj@>)ltBpUn(9dO^VC zUf}9&Tau&W*F>EWnEO^QjnVNXk4Yz|P@0;-O2=a>E*Ll{@)#`PWvklues_u3*7k_} zmQ6>NUD(9ySK|JBk>l^fek>eJECLOOcG@>+?wS_5F-+m5n?S&l9U4Mf58b@-yIK-9 ztWw|=$&Bf0(bD9&m|I%6{OQr8FAAE|3y;m=i~R3jakkej=x9%==PYN-3#|M7S(1Y4 z4=iNl=yYxIH2x!o>NHFX-NHBcCtYN?~L!o8bfkh1GuJNtw zbWL*ldn-Oo*6Hc`Q#!RLBzA@@J9m&HWQk8^gmw$N_}@kLPaF4t=KsaqTz}E=+=9-w z#@zJFwpomZOcswT66IgFI;S33pCtR;>7)GnXZCk{H_ES`pT6YV(|!s5ANxN3xBeM8 zXIY#DSFVrpl9r>+mrS+w4AvTX|K9(i{lB{WP97(XuI_-pbN*?AJQtMnfCJese3a-&u?|z8nvnH|F@2nGLKIFcJYssIjng^;zUM; z%{7Cx?A6_Ax1X&nUevZ~uYv=6$>Mg4i%<7#Qmf6kxESi%$yIPrAnbCS%;6};xn8O{ zU0FMdbwa#~(=xSZU3n93mAJZ5Z@~$s>0y_gq6LK67R5@ch;i{|Ob#)-owE4)!_=_W z!xaTTGr00ngDp0yF)ZISDL|Oj;gI#;eT=~!28^%W8$8)^Bo$aD39HYOcJ#URbJ4m> zo-bD(I@&t-F5j}PX_@uHy$;@wQj{FDm)q&9_DebGIK)huB$_PLI+@eC;b_}pNzh+)3iPHA#l$rcZNip^Ov`*v0Qj+$6W@YjoNVsS{7;xWrsH`a$q`< zBiP22py43hYoNf;qhXrX^7`0&TSn;%0Y#xFoS-mC2LqF%zlPQzh zwtiBZaQH`~bZhD&jlbv48wTD9Ne~g6^;+97ncew%)vuqadFgAuss}y_f9-mve4X&e z^0{kEUVnWM|9ekH{8PV=@73+kh@EwEZc%6vIl5%q+19+H3x61_IO+X(?GcY}UfffX zH+|olHLv2tyU6IbGw){VXn7S*)zv9dw)I%QYC7kXM4=9kqN$ceg_jIW*xG;89-nsZ z`f>L!?dkk_QWqkP zg%wgQT_n`6a}>Ot-LJm?*6)ZlnU?GV;&<=vj7u@U9{fUZx85uMwURflZ%zH{@cP)g zY5E5Wwr@PEo-ESt^HTZjnoKLkoCEWd!W^U)y_qh2Rm$}CEWric>P8d9M7+*&@U%HF zC?|DnYzXv@ocWB@1J$={T7CsaG%kb2B)%NV1+jZIB@2~%z zZ+cQN=d1a)vOHD&>Fkg4XC-}{9sc)Lb!hG-@tbF(Hn3fp!6Fi*yJx~om1>z9f!I$% zU3WN}TG^r%&+dzxAOG(B!>!w#Z=ATfZW${ZC%4mQi%0u!?QhAN!k}?GgW*P!ncxYb zMUocX^Zm7t`9Htlse9HOEbh>g=ldQ`nY}AIWa^ohg>m)U=NOiouQ+I8*x`kjx}7HLjwatN?{BFN6+sl->HI*EaW zsZc=rQzC;4=Rww>7LE?*11t+T4y3eN1e}kXdAoXkX)xpIpN}WJG5pdrt9kw2r;BFK z`ulQq*_kB`bNHk0%4{trX0$b1?}l4R-ogqCrik!y=x#h@_w{^& z;*=}PtXNp04wxOCE9^NpcF)a8&vreQw0P5CV6kRu>5YI!qa}QWU79UxCM=q8a^c?O z=v$E!U7fZCp5*PmapXjgL}i>#Y9`swV8W_q=As$O3D;nN#~Z9#Q&-mTbhpy~6L z9kY)J{B!%J@3@l9MVRqKj)KP}238G8iI7c!PXaQ^7EW^UnAujO#rp{x#TIKK2KNeyWF|$>8fhImziIVCNBL_ z`jyw}_7u}gs^#_?F(+DirVDk6I626Q8F{6Z`o0oPY_;Wd5D-w_#J+yT+TF2NSKkZE z5YSrGFlR}g;C`KLtIGY#1m`P03^=p9-0Qu8&9R9Ua&DZ)fj*Bq@{blT@jO3OR7g8? z_R_PZrRRBh>#t^Md4HTV<%`s2x#xEBPd*3UW0#YBz5Uva@RxI(&sLsaVNgHG-Tv(S zd-cXu$Eq&BKl*v!TV{Q^{W+VjJze_WdDA<2jgpf;Jh@odVp?aq@*8kSDljUz&17U~ zyU5$O(l%$`G|S%=H-COR#{c(k!QVCh2hCgft@w*dB_moR?{sg!wE6>6EK69TLkBbK ziw^=Wx*ipxeREGpYx;dyc5m|ZjdTCLIb69-vhz{V+0)5a8;+;WbzbrN&)pf*{U2YO zd-&=5sh_MLSCmG$Uc0d5{?BLkHIHT8{C-01oX1IGjj@0@Ak^8Z%9oe<_a+25r}>O|9JGgtSWZgTfdO;F$U zxuE()cj#2^wJR7L7&u%H@m6MPWQ2J{WKDM49mT&p^>~QnhJ|)>-X2((aF~xlVyj!~ z{p}3}Ypyyb6s!^xS>CkE^Up^8zq0?GYdAl#bbOq)hnas8!zB)(1-wj$j6}D_wO%^p z5Y#twqA_EM(I(Cw2BXMRTM|#t`tmN;-X!F~(z7cKmK`(`muTL|wkIn85t>gqnKe|KruGwuEJ<{q6|Hs?&+nwwSSW!1e^tA0mrfARL8OpDJP87oJ30VmTc0f58L4px#!Sj z&Z(2Q6wDrJK0Ea1_|+}(y~?p0&WC+dpV@kP(c#COqN<_|`@XK`SiG4lQEcxMMiqeu zBSr_%Eo+LKKa{P_|NdP3?%LIX4{Y7{Uhuvy&e~JMsDIE&Y}p1U`42&2voZpdmYh@- zdgGw5xXn{Ac0+FsXW-X-S^v-RlT&LxzJ8p#fBo$u(Z0VI`}=jzaWxmk#qOW9-TaT) z#X8C8xr+ZcoUdnz(cHH6OpK4!{a5)uGiF`OzsT)%@zRA0mO8z{4Oe0scc`yRK0ZCt zKRs8x@!3U{6#@}84W5UtMn<uI^f`Y9r@M z5nJ01IuvipnI2`ar$4}IYVoo|JttE=EB>9}+$5#F{Mx@4|DV5;TGjX0tAgA9-oyC2 zrYealDI5`uoz09liiBH?ud^r{G-f=@IJccb#;#&>a%F@IX$P0Y(%9sKlF zTb5n!r+=&NoZq!->B*`P|8vWxruu$}xSM<<*!S-F^zyIgKA8Ra9CLS{i;I^-Oz6W2 z7wYtN_asL!x22dK|0H!I_{Q1k>|wI^#ccL|y7&C(tHA#|*Im6H5mDTJvaa}E&X;d{ zs%oyiNqfEU_u9!}J~{;`;Wt;jp3IK^?zFHjzd9zSfsI z>TTi>`EzHlyAxwVn!;5D39d##g$ariPL)~SH|<}%KE62i>Bf_h8|D7ak9qj|R^7YY zo(|z(+?P%;GK(utWo6f6i3yT(dcYN=ccGz!Rg&rMiP^tzh8Jhw)LpLZ!*_gQa?JJa z7L_zzepijHyOQ$q;_`m(7PydgE_Z)C=kASDy|zZlH$D&l|1kA_&x`FF(;HJ2mzs8K zNb9U#KQU?fy_GsUUR2g!Np4e&TVy@oc9M)Z=f1lURgTS9y8NsgJcXvQT|VzuZ`_&S zd~qAwvK+3?P@mTPPg_G)ou0LR^1X~I@2wUxM>RY1nLMOIE3B$hosVcN6fv1Hp)_EA z?e~3hNq0BtwMLiQa%QgRnv`^;LaFV@-@3{6qQ|*)W}o9tbMR=m{f_+@i*Cl_J)W0u zT@tgpk^VTJ*W+io?$x?SGEE$6Z4H4+uIrT}FKPLI_GWkZ^5)2m3zwRGm~}TT&6B)7 z{paK;bN1~!gxCcp95mx{%2?!bF!G|~AHjS4T7e7-T{9deyO!uyYI-D3HGcidE}dgr z#&^H4#}Ulpzou#B$Lv*k`9-EWNFm79sORtOko{K}mYk1zmAGwvwuk-1vafO`J8YI+ zIl8!I!Ux|SS6-M{ewDIZUcI(X_^;HT8-6nu9r}IXUX9<*Ztl0N)kP0i#Ql45vsNdw z`u~F3vvJic_c>phW#VjiMDSn)SEWSpn#9?wH8xeR+%nU}`03574`s^IWF!0p9tLtM z+IffmX_(!=zCyys^!pP}a~|~1U3+GU=@k1K=gjywM>GA=CFR*u z*&TZR{S@xG^pWlU4WpliV!4OwjD&)Or83nwMTKU0&fRqE_xiV4zb{3GO`CT~lcz)e z-p7LVSO16UZ@z8*$vQ8#=!%xRv}ooFmh8QE*Z+R!EVnv1=zc=b;Yj6*#;1E^rd?Le zywlvvHBq=_zon1=*ZX`~%`(pmxPFLzY^X3Zz2vr3eAW(^V=t0@%S8E`rC4^HeBg4~ z;KqiEUitZ--D4i@{kkV3!7P$NnL#o~S-|dz2@As$cF}_W5A_$W%#a9BUXbm6NARIc zPxwLM$*V1Q@6?)dd0*Rn%{yoIWx2-vTDbMMY{`O8FGWpPZhU%R(ZO^zF|FIXx2QWE zZGIFPX4q{OR~`FG$}dK%KIH$eBOBd(CT~4Dw>o9l*IRlYZI)@Lbh=k-1|D>_nPGiK zpvg1iX&{ThqbcHA2PbWEVq`h8XlB?9r#|(}bpmZOB}!FS=rl{bZC&~0-K3>g3xmII z2{An_rQWM}?MY_uzQ;?Y-&)FD4lD}IES=vU_ITY@@8Wg4g+4zMX)K$(qWMPhVm5Xm z9`DAd>b?cML2R4}ZG8p>3_%h*8{D{Mmk1bfO5|M9{N?f8W#M+gbGEkx|8Q(wbHqtF zaz&E@x2l5q*~P44`+5HN*FRhLRKC9ci8RMA$N4HV?tV^n3p00fE#JF)=e&Dg3*-;a zICp%@rWG7zGi?4pm~-urNAuYoPiL&WoAhsT;+}PeaofsGpUughec-^n#??GcTwL7M z3p~801qJo}PyGG*|KaoPbNW^_|MM+NP-y9RzII*oOgECj=C>qufDIB=X>veci%R}%v(upUj0Vfz5W)RS(?r5 zpDP|)dGc#$bJnX?_5Ag<_maysU#)83WE9|;lF9t+wYlrQ`ElD7^E96M$vh8R?$R{- zVoI0$73Ub8oN?n=>+Pl2CVSeySY>?p z#D$w-R^HcdaID((XIZ^rF8@6D^T$t2(sNGaVc}p-HL=RjXkZcu%s1e6s=3^uBB<24 zp-sB+Y;)gL(b`JCoPBc)W9BB8{1-3Zds%S(ox-ncw`MPPW}f-EPeJG0x~kIpDnF}~ z_(0!zMP8BTPL^HU-B)on?SwAx#Oy;N-_rI6eATwB@7Pma$z$zzKTUaQFbe~pW|wEg z%9$I=O6T5sy{l~B?rmQrvWse`Z;73CdWPgI|3(|7%S&bSP0D>5)i_u7xw&Y>pYwa- zVy>vuJmbI&x#>ajA)m@@17c(2pFX`C`r*yb*xJnVhHN{_K9yWM7CyKpIf_uRj1i`|QZYc755VT>qHjJ(9vz}&LLB{6C4|L^Dj ze6K(L*!=&W-{0?6v(*3b;V+kwwNUchQ(<=cq2Q(-<7aO_CeL~IJZiG4xLere^<{q& zx!czkFSX6@y?Xw>@yEn4RoH`?OC8*`Y?i-e0!tXA7x=E*h z)my85yI!`(_tH6N?Tn#?H&C-!vKazEvSPqyt83;xm&Oa$pYcl^= z*W=9jMqWX)e@-c{HJYzH>&KK=zml(qe3pCl@z%eE-hYgO&zTix&D(rcX1>VB>aTj~ za`OH0pPkdsQpxTKsCy zDC%w%0 zQO_@v%GsYk9&7hI-q5&(_f+A|e#mfjF`TQIJ8kR#7+sl@zteb{`lVRCW=Kx5 zNO0>=VpW*SY^t(*w_z&7`gJ}efS<6H*x2i_*HuLL=C;{Z&m-}{u|p$>-8OqtKGlhx_0^%#kk^oZ_ibK z{+oJ3e@gYqpBc(omhbP)*&lcOt$#~i*17DvmFb^mO*k57+8nZAN5!0@)nfBc&p2*! zG^X=x<=S0*U)Q|+^7KQ8@AAsyv!7a3hN$^nbbSBZTq4(J2T_P zldnzdr>--9UE$eR_{H!2(z5gFXZ)l-hb_C ziSS$9-BxKH_oB9#e{TQObl=YKfVcXL-cZ_XU#KLZF37^RxTm@=t#FwpS zeUT+(bcjvjOm%_5S%sj$x)pU1b-S-0&{_3o)35&5Y%XV(omty+?8n<-7(*Ec?0R{2@?)s2O!Syw{E zb|?nlPYG_1IKW+9a7ZY8MVF?a$%h5CqAYVcUhPxKpLM8WQo_F<|NZoMxSvj*=`&qZ z_3c;E^MY`{bm^o%% zw@K(|OgmR5rg-tmDp!~1f2{v4eG`2|vheQ5DDOl@O9NIjJ_EtvQf9BpF@0jd(zqq$*i;V#1`dxCSJ+I`q?)Dg-VqEmJ>sd+AsWOSe8}aM zcA3k2-&Ea8f_#n#L|KYiR7E`&vt46Jek7>*+U`!0^Y6WlAuUgRTnw8Z2zaecnJ{;{ zhU8rzE(X@HjFv#9F2SbKu-p2nu7_vYZ921HKC8Row^Oo`dF%?G7v3+>RAA)L$rN07 zRg|ssOvWjPxZQP`@zd05W9+uxEG>F+Z`YgL%fB)+_AW2d%an||_CZs>i(T&yKC{oeHZP8m`) z*1zgcKR)((VNYRUu!>9Vw#N*cuG$Cec6>VX=!3uy3+mR^?JWPhr)!SQ=NeJ|4~-XA zmT^s=u%O|H+u~z_$=d8f0%D%;3Z-`*zkcAqZ2PrWihqr7wqE&V_S*eqZMN-Gzqhme zv$rN{+Vc8j@94|yTmAfLmF~s+b57m}*mO!ol4E0M+L}X}QXz?);!Ldk2@jmCy`2;l z*?4M%A8FQ{{91nW-`?-sU3d7mm1eALe;s_{|?EMz4zzCf9ubw zbDf@T&RuwgBe>s@iPR?g_vy&fXLcvg!iVE_60cT@L261(+e z%Jvz#c3b^sv#wpidHL*$*ee00Z~y7)Ox^bIylLH%eufAZl_?Fp9Fq)8Cr#98QdH^L zs{N?rT8x?_69<>GkpTl!D4(;Mp@Jv_t3$9Piv~mEDlX^51+ynG2`n)9w1UM!V8N1u zT!BYLnKUHj(v%LU9_CMCFq&h)p{&K&rNF@9z_4^Clb)nR(>ex@6Fos5EgeTLom2N? zX5g5hHuctFr8v)xT2$J`g-kG|D``? zoXMTHnvL=C##p<*hR1fDpI`NJ##g~T6HXQynd@Kwv_~nv^e0FCL+u+;fi_Qc#U2D} zWKCGnIoUAj*=bH4_8l7by?b{mA8cy~?>zj8`>D3m-oHC!=Wn;W60d#wZ_nSFP4jY( zTg`v|-|XG}zB0$%EArnd9GJK;vR*}IpLW_>+sdZcW5=ZTyn4Px|6jaq`i~`g1rx=M zx+lG1;$?Blp6t2JdRNwl50}y^OT>fufBc^il=C(9hkmhTO6Zl>z23cxZcj{Df5xnH z#pOkZtr~C39I|m<{d!^7zBeN0ZW{`RvsMedh%F8aOVj)YeG@_xPRlb-)QH$~#OLeHGejrnAVnEC|X%CW1579qe_EPmzRp6U~8pr)|%wo zwhAog^mlST;%XGLTDL@K@*%?xFICapH9q+FJ`(>+wm*v zcG3OpMwdU}2JIrWr>Z0G|GMS2iqkcD?LxP%Wj~r{ zZQ9JFb-{a8z?>U`cfb6Y{ds0gI6p&0n9!RA4XQmF47v+~*;E`VUl}d+3oiMs^>_YK z>n+vqCj3{jSrE7EefQBHQ}-FkoK}2Y=Gpx8#jT9!xfX3!-a9X7bp5{b-uJJ}W3G20 z)*&ae;@nz|4p$x3*yF<05c{@ObgP%oPgS1XE0$~g^*Z@+Z_kYV{>u)%J6|C?{c_*& z!@J7AdcS>rbl)*7+8z&uz$SwY%?9`!~|^n|nxN z?S>uQdvX*Q#FB$fCSZYn(bi+6hBH_e}2Zq-xwt2zH>Q!7(aZ~Ez2mS=UEMa-dr_so`? z&mQ*|{xhfvnLF|4-DMzzLU{g#lPym+m&1KeR{x@enoaC4hw;diwhR?NS*MS8gj{V^-up# z?_aFSd2RhOKA|VBK6Z~@mr-AqPR^$vL7|tr)UwvU_^r7>?##lx!)C|AF2An$*Zw~B z>+PlY&emPEm7TMCTG&+c!|n+M0)}7gK153jxlUhMUtFtqZ9~h2vgy|L)1MxF?>PO# zznb5L*DfwiO>nN<^=8)bo!1;>eGNq=SI97Iz3CvDeyBY3@18%|=N_BZZJVaEcU#K( zjn5Cruhp6-eu#JL!eu|Vo-R*3d*+--ZEu6hipeSY)%79Da)okzTaWLqi!%>M?e3m> z^Q@IjmPwXD_1!a83$JAOdPQ7FGYUEvyI#32sr-talo!J`vCBtFg_J$(RFlLtioTuD zWy|JIu={+g{ri%P4XO*gZym4vd!qi`<@06zhY~CDcC&tKXudzyy8oBqhi#tkr+>d? zbaqzq*X7xKG7Ha`e}D3N=FQ)KuVtD4{x!GqeBH`F0^6iE2PsHnsCbtd1QI;Y9oIO_QHBPWd&5;?=J zUoH=LRuq}M_V3$=KW}IL-y8SuM^R<&@{NCd-mcA`qj#OJv*4)1;@oEOuWK0|XWrj< z^>lWGwtPmEUS;8ZkNn4lw>Gxjx0Bg7|MrY6wRzY3uLrNMU6LI1GA)x&yF18EQ6;?R zlXGnQ+G-oedCPx1_mY17W9gG!yPg$(mh$!G-@WDUxo4d79!&mG+n1kqAiyD4Xp+|( zmh<1^;_tqwsoHbn>&n`%?)xvE>{{w>_>9lA%<rbxy zR2z2hvw>`*;T7G921it^Uo0pKVq86`O#gis`zGCn8*$4rLb?jS7slQBb4T{q7M^ge zk`q0l&b)UVmha=YOy4Xe$?sD2a{AYYWgj=b(mTFkru)ASFSqYazZ0kPIWDvA^W*J% z^77iZ+4JgNXqtL%(~`BvJw?1Nr2>mj=bn0fwkmdGWz4l7Cfgrh{`E;eHaxuUap|9L z)!)1Bf0Sf<60qox?9#&`Ken^z?pc1o>DY=Xo1UKxmA-1VX8+CW_X{8Q^U2iTImY~~ z_WGi@=Nte1-~RAx)s2(!Zq+yc1l?Ng#C7t-nj`W_t(O}2Ce=TxFZ*?I-X-_;U*5g- zlTL^J{l8@4EGfyvEHRynnG0_&l+E1~{ooQuv3Hk``p>%?KUS=@|F?br+5e9Z_t)3# z{dfAd)Sqqo>;3LO%P7*__vLhe{!I^A@wH-Bfns7>rzd`XcCzLA-A{4Fd)9sb|24mF z|NblM-%o#>8#iw~`}Ribw9QL2O=RzW3wqklk)7RIU_9-0)Tx}^w#Sau=2|{DejxW+ z`qzCH(=E4eG!*^6j_EAd#-7_XwRSD6J_{ocztOz>=#;t1%b%=|e%F?=he_q@Fd2;{HTY2~S|Nr&>FZ%JTd97xH z_06NVzPzaZ6n=RsTb%u)Hj54>&vXs%*z4QR-%9`fNiDx$e*V6o??1oE^#4(h|JVEf z(dYeNx1Ya1{p-8)k@nj^ujkdSJ@DG1uBT?Tu42jtj^2LuxBt(Re^vi) z|Nr{GuKMriL)YcwkNy9Ue*S{!d%Hh2-}?pMU6sr8o?=v{5boVw`El)?jn^&q^s)EK zdEf8n|NrXzQZFBff-+ue^NyKTN&^!@(VJLTri{r1r$j<3k?`M)|lucNtz(7s2lSKj{Rdt?4>IH2|U z&i{Y?*YCdnf9G%b(XEr*^a@%Ioj09QwC2msA8$Y0SoZak&DOmdX0K1YeWT}IFZ=%S z8vbwbdG&vizy7Ssir?NJC%0>(?EW9eh0i~ie9qLSaOl+($7xd*RlUBoc2H*@Z%bAjo-=W3GEeTyXD1{M zf4#HczIvX2|NZmze?H&1wsh_1=l!2wFAw=9xBi?K)8vIrMq1JB%k~^uHYF?Y=cOvW z$!oKh|NZ#qhW+2YXYQ8N{Q39qu#K_o1({sc|9dByW4Q-%t4&6PWT+sA$XDD`jt zwo}O+Y_ZOrCxvHhKIY0_H~)Qr{kgZL&*U%apKC7q*)w;M+gX!b5wV8Dz5d&4I)88f z^4)UwU)QQ_Gmq?*EkCe{$603Uv537#Q@CA1v*VXaAHtMZ;{d!`W?3Wtx z`JeY)E8aaXfAc-=w9~bg-%m_q_I9^cjygEKMD<{urRB}7=QLmaw?7qD)%!x@kfz$j ziHQpe3eyfBY_BLZxRJF^)xdqNeASn{>dkYtpWeK8PwU**uluI@hstk19+#$Y{)NGc zB{ppi*R?jXNq1&nKX!J%-SYeA#8y9j{O8B6ee142uI&u0eqYr!#dnpX)wv~FK`MHR zB5anMFU{-;O%C07GEE`2*lyXj%g3LW|NruN|8JxHe?FbQf2{C|%vZtbnLVi~rWag` z*Ceiey7lCq3q{%6_-kt}Z(Uy?c_=jNwi!dNoJ;7`+Su?_2HKWif_SGsJm9LOT^S%y zv~W_xq`MoEChgvnab0llwCLt8v8cBekNb@p=V_9eyY z=?m{h)X3bOFI5H}jd#8E2UywuZmz3~nqw zbe!!wXYmb70iN^kAC#HjKfN>m^NG(o7O!^ZY_W0MAGuc~TmE#0^47#@X8ET--~RKu z?Tx|uz$_nm-ffj?Q)@&@6e`%6(v~VM6nZtWN+85qPl}DX@8+`OclUTz_P#m%^z4tH zxt~_9UaLE;^hVSf?O)F-gkn#HUN`^y>D)`*?d9#ae}DTFaBRXnE2-$8-d_dcj*7WF z?R}HyV?5>PP8Mzlhd7tcfDJ5*RynXRYE)|8t+ICQ&uvx8|5_oxyuW|#5(A;gwN*m4 z;=3M-?#ty?c>Cq+#=rlzUDx-Kxy-ou`W(-NOG;FF7In@FD7rl9zH*E9bEEzqM?rxE zMnw+p2XbjD?}O_d=hgn)6{~yN^KH97!{N>fh7)sMdiT`U#$VsJZQpeptw$O~HYPu3 zu1)Hk6>IXY`kZVgukzb{?*35$#eM=hk{%t7X{SQMWlLAL9RG3pyS%A(|HSW%&J!z+ zWR%xiu@NP^5ihhS(d!GU>?W(nWW(d0ozIoFlMRluKn}OUHx;wy*%sPjh-W z=6TOAvea04VWK;O^p7bEWA6XjZt0(3AZ7gGjh;MV~_^GY0t-e@w-sQ%MnxwWh(Y~MP^6MB+2qp#j^($t^5a*y*qpJzPE@v`sV z2)p?%nZ&)|h})y2HA(vH58s&eC$&wzSFDx8DXVK$>bG^Vk*QsHW5vm!=^D#@vOmbI z^V+7OFL0skTvf^3-J8Bv-_$)Pp|Sk5m0Pc!`0XgQt^!88CvDelx!SMH%9py3@h#-= z&&VkZkr$@bp9)y_!DZ%h-j|vNVu#ba4+L4P_uboids2dB$Ey{`EoYk;y9u53R5HEk z@Y%#qcrMTOldhGmt`18sOm&m7N@Wu=pT20xYOT|T(Jil(^A{Fy9aP%*>6K1T;J4iy zq8=r+$=^S;-|U2-=;ecnzton_Irntcg_ z{No_kQ<IJ zVPDjF1xd43hBH=Dnr-hw8(oH@hCD|;uQ*`R!f~|t&zjD}gxC;nud7yQ zOGFr6L@p^gxAIim<+_}R^yK3uPcIZKHHvv~<4#PE2!Hiy6Cp00@*wSQ=hQ40sTl5% zf+bByt*Psjzf5+}mnyRhB+iM|5MA%)7ot`$B)8Gi9T6zIsFIYsN8QfEKqBClNz~2t zqT0u0D>igDDr+iFezq!XQ*C&$K9}2v+%TK1nD4D=Yp&~Cl>Eb zG4%5B*cG~VvX;PM#|c_i$qEM@%$}q;q+F`}F8empfpMzIYnLEaQNJ1?=~RKBq|BEx zE#BOo+Y*>Wlrk7Xd3z!)bhFME$#;I7ykeDG$QhxZQbJ+}df#+8pU81N+7b}iVS4=F zL5+e)?=_g4nieq%+fH;yat&c|($m`Xs_FIuVM)s7V67;UIdgaO z#Y(n9p%{@4=QrzgvVJ}Zc(=5|wxpSz-QvXLtqvKhnrw<^x>_(UkKIsEv*f&+(!WKw zv|1(v=B&2n-Lz}*6eFHcf!;m_oh;d}dmnTMoMR~Fcj9|L!?&c2-Dv6~Ti=^n8XXo7 zFRK6UQ9dq_lx6wRpfxP4xW&Hy-_fr-N^(X0RI`M-4~ktoJK0ZXi`$NcBJ5rwJ@<`o ztmV5j=R*JH{pbIsL>0Di?CNk+=wfnGOMl%Wy`;-mC3&G|OSAmd!}FT6b;M4*w2AFn z@;7hO01SVCPEM+v`Jd;cEe1F1u$16$3Mw0`2JNi~F+O=c%T)tT| z(n}^hG89nI)tAN?9ayr7MS;UhZ~CMo27fkh)Y{Uv_ss(i(+$q4 z%bd<7MRkQ+>+JiI6r)*ta^sexVN6QgA}-59EZFqt+Vw2q`^v;^@<7PwX~!i6NtLVC z+syAQn>{z=&+1P{Dv!o1uy>ruj}&q_xKibW?gLl7^M5?LrKWjs`7h}863*xRGgI!1 zjnvg?jB*A-Z@Oj&wq=xw9(%N96SrTAYx9g2-ifDTHCd*tWOU{cTWWf2Ig82Dp9$Hi zoxIz=F4nwW*Qd6^bDErBnq}x-n|TpOG=DWsT*{XEoMX4gjH6G#E`KJ{x=^s(>SmXZ zURtBI-Q|nR+zOJUR|fNkIT}5@wL`~JNScFX_8lJ0CMFi`s-G*HE-YkRcVfvpDF#s+ z+x`{~t+14msiyws!O@pYQugeU+!2zclPLQw%aL)HZztEutYfod-P(+mvdV1dIvDMo zu5{epx@6g-$iU09WsKfEN$5W5EBr!2cjJ;+k!?pzLYP!pwk#1gcGqfD&|->R5aMFu zr1o9I{d9il6Cth%=c7*UoNraRTW#g<6C1a9{m9vREXL(xVu#SmSps+ce6KB+a%z0g zwSx6(Y{0=ixBsQRz13Id9=Ljk_r3WB?I9L^ANU%MOLz0Id*2DU}dYDmmh*>G~L&Ors!k1qagTG7D^A1b_9 z*}~Dlbs}JkXzcpTOW6;+JS3fxFTB5eWm0@wyQdC+RJOOM!O9m&f{NxRv|@N9eVw1I zFe@mWG^bJ7&ox7$rD0=ZTuMQw_n{UCHr5!I`i`Vej7rQ+3~WpZ3R30{3T({@2b>;F z%-0qP6!grgPTC;TY^1mLXq2l-TeXJKbfe}gLEV$)^nRU|D7Jg*48bEa&y*FaEH0m@*;XjY%El=*b?UbbZ3;IxXkL1du(i;8n{yfKy!q;X zD#MbyBpYQiRgRwi|MU2NyXYf+d&_*woxW%^beYG0IeQ_5qf130XZst2wittysogfU zzfBSnuazVVf4OkY zi}qD4iBW%>`3eu}pPcKnW5VfvpZ6)RtyNZ6r=8E)F!AIbaT8Y7TTlPLzqVpcn(0#6 znh#D}I%*hF7G8YpXWh5*@;MJdzC(w)6 zK-Zg7NjeA5GtLaP{2Eht>qp(}|M_7pX_`z8VvpzOU1>FrJ96v!Bfi-^9&HJiw7U*{ zs20oLCCqqep~Fi-#W$`t|Gxa4_xaOT?n|NVnc6qB-Z&`n?s({Pa<@wE25#jUCIUG} zZbd;LI`t3CFkZ-VT{GeQlfK>I=1eK-Ld~<(!t@nZ#?GI? zn)&V27mKQ1Phsu3maSorPu$`v*W4Vu&S3RS)%~_>51L(&P+QgJUA4L8Q=rTBMOxq2 z$jZn(Iq~3<`;S*PW@S^-GQyJ|L@*utC+8xf*|13A*W7>cshS(iZEPh3G+9q>Ii7fI zPF`N1py-L9M~k(rf1O+Msf$-6&h0_7Xo}!Pu`{7ZldYb++E1Th6RA?Uu%Mx1invXL z{o1}Rzxlc^ZtJK$;1qkoa=o3azp>|LpWd>zD2_=fdCy$R4yyi_U#jGEWC;^@RO2T- zKW6rfy22hw zznIBSan1h?~FqJqyfpR>I_PwrhGhX7l`;TOWs{#oX57vx4L)<}d!1xc|@ z7wMk(?rGqQ7hYaIVxrADl^bWYn}57^>wLrJmD}v@*SIeE>ZjEr64d#^Y<+{VwMX+* zC)r1nbZ2%*_b;!A_<23NdxG1m#u>gGolBHT6GSU(eA^srnJiEA6>KnhEjKlARp{-5 zks)_DH}BO-*?4iu6^5ksGBY=qa8&CZ+o`aCFNI;IgVA-jx&2823-$;p@2=sNp7>Nb zi){+JM*S&+8 zB(Cy#8%Ox2EABItRPq*T6J~!i;R@@)u9La>a?c&Ymz^oT9oXu-?!wgt=}&Uj$xhgz z+_LUd;GPSnug{4*XSnK}qI;oa$=={?Q@GTh_JuBEH8{aA$8goDkh`MQ!J4hQK3js$ zh80~n#j=RcG3utnG}{YPZ~WW0z5LgG@&ErLntgT)t_*eA?z}dIzaFlB5zz~|_%7euxQKbm zinltmj8|=0&9*UevO4>VLrco7Qi`RQHk^*(3B6+AHccqBY?FJqhu|G;k>eg325lm0 zErIO3Da&?zir5sWEWGDf@Y4;ZllUbx7#1El=pbUPW5C$3xaUvCbr}JN zOZsD2W2U5PXfBm=D_)$uDkA5LLa5JAy`^y-CzJ~}a&KE%p=GSMSxz;EYA`ZH ztBA0lbk=a?ikK-To#wPnVw2RA=w+ezHeN8XDtLc!nODcx{ptVXr5{(zJZb2Nyk4|D zolX19jrSL%E1YJXjT7FcwMv0!p}4a%OWLZ&hEp1fD;{t8?B=b(d2EV7(;xl%H;B3vgA*-MbkOma-6lJCJEFyZ8&g5q~A7T<+mx- zAy3yFzrD1iom=h9mi&cPHc_VQu7CdA{BPqf)1X_rrLU%)jq%^XaW(4d(urNC_Fccs zZprw>;{5hk`H$QD&?1vPB|xkzY7e!Y!Jz1lp`k`WjCpt*KmM{_&;b z^yeR6&%V!fS>SVu+L2kD-uZX-ozPw%erU$z;K&NKRLQfiQrvH(O`V#mpOSTSgRt2Z zlK_#6emdv8FDacqe1tc)?RrnSE+$JRFgz{}Yn9zn zyyVgZPtWTJ$Abok>f2YC@Vgz=`li3~da;UK$z zarooOk$q~}H?_X6e0|npZM0*M$FYvpv)uapzfYOrV;&jXeq6tJPww~ktaop3d)@Qa zu4=Ju*vcn&mbMux-`L=AW8H#`8+(?@&()XVxLRxd>+;_HK3n&^ervs7|Mj*c^@iXp zI=0IuX6|sCcFbLMvUi}{<_Sxr{Eo%4`B?t`AtJihQ~kNI@xB99?`uBaFlcY!y6{wH zD$j!Ot8tO{E+?8#6>^BS(PZn`wsXHv`0Cq#UWUCCl+2yL!F{w)vykCQLyH!hx|rq~ zi}vEF6HdR{<$Y4@=*oLrN)xW^?$kaUb9rI3!SujTn{^A)ywB&JKXv`_f9W3)d)M9l zx$J_`?>kdI=3kG0ey;w{x9NZ982|ru@Yg1%%e$UTxa-d(JlA&7o#SSwt3H0PI-Yfo zZ-vL}n!|=Wxj0=NZy9+XlvUL9SZ!QznM=oo<3`%Uol4EEIb6p!Cc1`6Ffa)A@lIT$ zWT$9asq%Qm(iQ>6<1UiH3#KZVt@&MG+|eiPGST16MxjsVsRxG&UwRU6Nr$u*wlzI;>bk(5p;hw$f@ zzdo|P5?1)QCiGml@Y>B!ojMp(C#<{t+eP&}?-gOm#a{C}N>#p>y(!e`o2K0D@zO8* z-9O(|T{9$(>G;a({`}$bvh3B*nBDIZFRcoSDD%H=)vuqtZ~dD!e=pvAe$n?@!LPIB zFZL>YypY2Fe3tE1gH0P3-DI@zs4jc-_LyuzSnB!B>)qbH%65JH@z>_MJ->7%I^#0e zax-$Aez?T{Y^sIwrI|&3zk=n=jgCM3_iA~u>TbJBRZG$jD@pB?k7bi{$!T4iVs6f> zdpzL#vd!zh?6eG?se0~6;skZ)t2=t@w5O#W`trE+(aq1vy46=rlBQbp8cgk)wCb=^ z>B!A&~kC@0j0;2Uj$u$A(A5$d(G$GPidR3 z1z}+e-Dc@}#Y^m&=Q#1bx4&ZFN+X}Z6_;24;OhG9o>&}lezSR%tYnMlrcZ2Yliu9w z_n6vi9#9i`_^{r~7*o$imE(oqrL$w!uz%c;%$B>VV#)nb+g&%K<2ws?WWNx7{6J~S z{$ml9#|GfI=*XPW=%%;!%*kSbDU+(XrXUea)>FzAv9h|gp znE-p<&gHjvMy^|a`A^%d?X!2^e{t>XOVc?WDTjnaRMa?h7#?cxxUv2B!>_x(oi6+R zr!;PVuYCVoySaAvmrH;C<7j$(_Me@;{J#7p92cCTa#CjRI`%L(#qiEq=PpgwgnM#j z>(aZnd3&$p-}}~DW4HG1GkYVZlp9oYr6w^6zAoAMbJpF8C9}M(bsw92)AQW+s7j(H z;OnVZR`QScSY_XTyIQ`+qTjyzXL3yE?-QD*9!mUG+2hmWnYZqC4y#LX<<58g>UXbJ zEUBNj{rT3((){H&&VG@uYL0y?lfxR2oXo?l+wprAr}LwG2R6@I@-}kqZ_9d}t^H*` zoAt_*Ycp@J|M+-MT|sVN4xd0s3QzY=0aGWZ^7X%xAIiTm|26rr@ZZiE^YV7yd$qOq z)5Dj4O4c?e?=qf$yh=?}d)fXy*Q&UbWgeWp9hcpz9jkfrrPJSKkArWq`MzGfo?kcB zF6Z5<&K&-D`8`QzlTLiQT&&sl`^=?jz1nm5ukmfKd$?t`&`#ZZ?4OPFPApyjlp$T< z>h!l2d+j&Xe6xuUvk8xSexSo;Pj?;<=b>kw+fQwNAM@G&{p;n?aryWCrc388{W9Ie zCG4V+pvuZu@BVa_emT7|&;H+o(l4598M(|8Rqnoy`o1>)_xoK710*MTOr3kBHSkpA z?!XnQmK+KW=T;Zks%iuT#IIiS*d^!U|4Zc`@9tfBd8blv)mmZQKErEke7^IEb1C*^ zz4=*Kz4+AJ)61Tmzq@y)q50{b|FiO<*6)AIvT*71Sh?@pzMlW(dgx`@SLWIC7u_y< zpuy(Q&a32?_UDxB|L-@~=$G%+T=9m%U#jTqoTM{S_b;zJn<>r2uKbO2lA2?a&%+N3 z|L%UPpS>k}w~?R!Th6coFY8B#mi07n1k7MpSJ}2adiT#+`qSGt+`e9HonL*wEVn<; zcD;OA=-ab0HgzYWlt;`_1#z?bRz^e>-1( z{O6`iB!{nmaD3%d02UiZ1Ydu#W5{?;=1d+PAsy#@76 zp=WPL*UrBleb^+!>vMzl6gw&eMzcbkkVO@jLFul?z{{pZtD z?>bu_S9$FJcV^Qz9fcQ;nf!C6s&*8Zxbx0Fp1S3%*w=5CpJZpI7jo|0yLP?&9JBAU z+ty#T+5E=oN*K$c*)=KC&T{u&yi(n4z!G>?^{?3e=e6I$OYUw@%c=8RS3bM_^Tzns zD|2|=ou*Yp7DO%SaqeBekX`CnV(+n_i+7(V_aA?Ad2jr?CTEw{dpGP`Usf1AxP4=S zTH+MtNNq2Z#?Oo$zUB76*%Kb;1^XqSX?Df7BB>VeZDE-ud;)^3vD; zf9?~D&#zd0Y|&kt6_2Al<*$^ai8B~_aX6fK^O?<&X~x-qFTc$1efjXe^yIc{ejmcp zij8ZlDhpKSbgjP3)WKi1p^JO>?(Ex-Yozo1t5+ZXSbO>3>fS3)Yncqz&WL8betpm1 zRqff2PJPUtX1m>bXEbMVNix&B6=j~9($~GW+sbHmZ#)&>_~rUj8v-YvuP0^uu70*&@BVfA z?e;*!=ic96M-^RPd)Ub2bx_E`gYQbEB%a}{1! zOOXYeHlBSLb(r;YR{8Ss*Rh%!>!e~0FI`^vy?JfHjbjCO+fpx7M%tzI*-n`pGq3l| zv*z4YZ)feQvfXPjW$WBsbrp3tF2wk&$(?-mcVhnjZ{PjjnDt$K`|kG1)MZ;G*&2$S z#jhtHDB2LeIzA(-=ZyRFjRzJ#71R8DKx(c2X~%h=O5f-`zR`Bvr}9pr-}jT5+ZRV~ zaJ4^TuT|T_Z|uHmt<~JkB{N(;a7-z9*QdboCZe+TY_^@*z4FKW`zk;0E8EW}x3>AS z`XQC#*vt1fUVmS||8LEYhfmJ@-7H*xW%~chzb`#%@08yCXem7Za(%aNvj^vw;so>h z=PK9L{y$|cWSgz=kVj5v=UjW4u*0So1?GQSvP4Rd(Sza9LbiVA?@?_1oP?6G@RS=%oM|CWkr z`kunbrF+#N_Rjs!XP+?#=f(s~|Gez;J)hQ|T}9c7sk5VuF3+28ZxQqD->PjtU(R0B zU%5ZnT_EJ{NsDtE&)QG97+`X}yuUkn=bYOrt0Nqj&zQK>C9eCv75gS}^93TyAKlq_ z?c84J{=|ECYeH-{iJoPRw^l!8t*N_{@kVlm!ZoXF(@X4&WaLh@pZ-;I_G_M7rxj0V z)8ftdSKXHU9(Vcv>F--#MKw)&sj#+`ll?RAwWsISZi{c&p#Pk0M#j>fG|{Ei#`)Ro zoSUy5^R?c?)+Roe=fPCR)A#1J|4ZC(xZ>RPs^5wBTjWB56i=>d=YDLwZSAese|BBv zmyDR%WpLMmC#+LIeR}N9bPoR0xp%g`AociMKkpubi1*?;pv*p+QEM5Eiq}e2eCc{UkrJii$GK*9>A7kygmH+K? z=BkM37foASvKIEm9m~8RaJz1&O^$T$>C@4%?*rXi*JXy07Ya=+yM6?t{!M9p1&^GnxWkG8ZmBcxqBj#985E0`3+o=J@-oe!cto z@qZJ!$nAVxYtIGFdK|ldL8{xASrNzHMTAXHW|d=bIiTHrIY4EjK-dJ)Pd8d;6}P{Y z(GNRT%C#Ww)JNw%^LPJtV$|Afq2A?vZuU&;B2j^x=gGkTHwthBH-=bQB7Hr{pTUeCS!_DkAv z%lwz_W?lAM-!t!gw&LQw+Qi#;Y;CuRhHh@(#H^3ODNLJwH&lbAR?N&uY(8uOz44UD+EHq^Q=SBV+t# z&2`&&`MPPx1G?7Sz23JtDY{+v_iTld=55TD#q4%&9j<~h%sLT##RUu(mAt3PJ+EbY zbmiD%tMB@z$M4_E)jmA8J$(1UFDJe#FgR{tWI1VYUhZPYr3b&h<+;h%^Jt5fJL%c3 z5-r-n&#nGFD&2m4Q+Z`UaP{%GdcQXAk5tHBDf!qZ|9<%WFB`jgFK|6yxovLsH;X-u zz794X%@G1;5@0Wf4_xsZ}C61{MpNdkM!z2_96$GbUY!oljIx_#-wZGAEXHO>e zsV(Q1R17RXx8{+g3X4^Nz*H8kK7RACbk7~f{`}9Gx6kIQ*s7;b7+$>5)8KS)Jk(Xv z@$$hx@5$#2tFE`_)P9V;XDP7G($&-XOm~%(Z^43lmQvM4*=yJXZU5cN+q&lBt8d-1 zVpr<+#pzE!{q)gRPMiMdcH;#1=pDv-JdD$fUOxHrZpW2}vkK21o^GcXdUKoYl^L_6 z<Zal;1&hnz*nx4Cs?E-o$+I{alu-{fmMGEXPk-#NGYh32{Mm-l4l zNeDQ31tmI({86}N<#_t_47ufu<;uT5@4Nl_etq|4mj0aUyLkWJdu4KD z;~Dm7BNG)x%{g9Y_Z~=>dUEk@aC}UG@ZJ-vSFYZ;txWdj4gNhEj13wmzNaWJ4=XiY zRk&u?!OSdu_Uc>nORxR52=eohI(_!#+|NFfj%O`6eKjSqaH=gKVX+0W(Y zSFgGK$!cf+z0B`1=2;FM36GA~upF2p!uaLmw614UrTt>>-V3Qrye>L5qMdWWx&O>d zii$r~y{&r_C|kF1dACYX>e3X}HAP`2k!HG5?%Y|rdH$>l(k)Vo8+fd2jr9IK_*-=9 z$BnH2FAqdU*?9=3&U~EnM8+_E?w@njrc+OU$vk3Ou&PH-^~J1;`LAEk5-dJ18QPj# zxlZy-=i3n7r2(=#l{PEy@-+}FzB&8%O!Lz3A9LUQ-?&{z-hVG^R<+-AbLqIIT@%#> zaz7`0d2#08o0;t9pDRns_`xK*Mgq#VSAg11z1?QeGe`tX#1>Dup$_T2cS^*NWhnbD<)*-@C0 zLr_VFH7IlL0+lr@pC|Xk9nL&Iz4V61$?R9955Kd-YNy7$U-v)$ab?B7kk?ndw)QxB zWwuXy+od?CZEs5S^5bTF%RedmEZx4gZG+vq=e$3~OpFgG1E9N4gtkdS^B_Z~nQbXWQ@7hwuHqe4Fw1xm^KpIEVu{(>eg)@0SLJ*T-fvx0Rr~(g;`_R{_sw__#^)lxXw|L_F852t z&Yt8bxZF2s)dc5fr&w(l8?WvDaO2qbI~gZ7mgVn{*>3;-=UjiDnD)!7@9C+!s$6{$ zdU30ifyK$xJ;(Rr!v4t|OYYW$dR=mRZrSbP{Q6?%l|)gM=fA(4J)E>!U17qe<>C^D z`({3mcwxnzyWHhSvzYIO`9_8DlWYSr`aU23@vpFQhu%lc?`^hh42EXWYiIw7da{;j zX{>cgPEkyh?iRCtnWsK;6y3VRuKaAye1CYlO-zFGXY2Rw|NTBX_V;J`IxLi)Rg<;) zi;J`E{2kusFPhHDY86?;$YS$Ilg04kG$*G^K9@xOJbx+WOgkOYXI1^Qus8G3^w+b# zP1$zSJVxn_aAfVdnQI*iKYiP?J^k?Go*Pp*On3SQ&JN8kkWgN6eC4&mvgc{R5i1u} zUCZk8Tl%ErO=JaYhex;kqV?{~FTGAC?yHYDU$?TL{&nDzlCu97O!(JboO0UO{pH0I z^R{r`7I;v%sLNLM<+Ax2^B9CuTobuvO|&8pn!QpD$}Y}UxLvmA*G?Pp1+#mCBlzxo zdvq{$s})1KtMMBH2Hp*i?o_C3%BaXVyzudqebTpBv_h2n6RovEnp~DQI9Q!a6iQBN$YA7}^lD4g-8npoD-}2{G)?1u`>~)^JbH3n zne^L`peJwc)z5tEc>mn}aDV@sEOPzk%1J4URMek&s{O5df4{6V#`SE7LXhm^ z38IS=7*4}$HXVuT370o-f!0lM%~+OSMU~v6>RD$$!3YtUcGz) zhk#CG<+h@=H_Z*wF3jS3@M*I^!7`n_ar1n#{F2w5d9=qk!n^aR&ve5TmsVC?a#pUr zYN5zbXR-Tk^NV?FIJ4}^(dlBL z*F3bD9&THxtlawI>O*esvu&B=BNSSW zB?Lt3ylZ@tlhL(ky0mp-)uw{8V#f_O+F9+tI`6IUngc%;C^iV+J92cROzXwFj;EAf z->_@jtD?{*(zba*ef<;p3G>ChuCCma5t6wm$YIV?UMI=Lhx!<~6O&wmgmjxyqvXRa z%_81c`OV$7Z9?I(qe|AZdgnzbFS@3(UaNWL+6hV$tNZ_!WQT}n^=_Ctbw)svONzZ{ zQhU*(pECZ;JJ%_<#~fO4syI~UmAg-7X_CT3u1E4$Y7FMf9rick$dEg-V8Tw`_g-si z^RgmOCqGb0UUFr^(ac_fr_7v+LhUh;fhCjmRa!P^O<-dX2oPFuSc9R#Io>T#>*_to=;-Wt38VlA59_Fxx40cPb=Lj2beQV*@t`_7} zc7rX-AkjB~A?W~D0TP3Ht7_KHpxy$b?mw*4s)Gk%|7}HDv>8MG% zO+73NpQ$KEe@ZAeeD%j!wKV0<&MtR{gar%^jE&YeZp`;HNbPL7xPI4n|JuOmE9a&b z-w>U;cXHvh)upp#x)e*!9W8E_Jv5;+^i@o3lt?S5*~AowV=M)0e!G76eHR{ZSR!B= zXZZOQeQpnrbaAK%hbF$~_$!>FEMxsTMPSnHxF)B|JO{Lnba=*w8$VhjqPoR7XsTC( z#MV_t+wO5IzEAnKWNDA;9?zitpDpV+z8qcjP3xM&&tJRV_?a-UFqCwyU{DZo$P{2; za$$CO$rb3q%)!(W+7ZaWq^9ifYWLxmtr%&A>(=OwX z#2_Zc6d|De_9g?<{J`tppG0;Sot3NITmIMZt0xyx50Mi+wu`Cmj_L*dX@u^0Tr6OB->?f|s6Jf+^eY z-b<8TC^Ahzp^!nq*e%u4!E3s}&DDOj@#TN_lr%9h-r-?7kk(|fx-TNU$vfGBQGr40 zsL#Qv3`;*xVhFMm+0b|YVSK>{sdX(y2OoG^S++}FGMS)M9d_f7kEeD-MH@_(0JzcYJ!z;mngmL!)o?DsospR&#rJH1#tx|7|7Q$grq znU46g79)tmHYW#-(|yw)-!;vzySnP!3=PXpql})dyAJn+dphbX+>$Kgapt=_Lp3I__0*(E z9$Pgx`?##`ZQUN;!Itp0WmCh1(1%YBcDPHc>TQWgS$T&0oa5UBU6qtp%?-gzI!sw& z7Ca4#eA4BW=#j}Y?U6;6&0@h+&bPc>mZ#MDrtF(~Z~g7$0JVFEOcX0-GilV#G-i}% znH$Z2j#*sh&-4Q-8{?d-TnZ=fF7h;B;hVE0;>RQDwgbY#oQG=axE5UyV7upWNA;EE zGb{EIUz3CY-9|4TQ#TF1x4sRsZCb9ri(X8quf4SYMv0ob=1Mln%`*-$G3FgO=&*Da zSC2~bvBxqZnO_B(X5TW@>R(V8bLw-$!WTP&j2|01&s^lpq3ib8Q*1BOp@b$u1wkhb zhDn;9lSH%*X-T$=xxER^zPP-qiHAwRMz!?F3U54)Y2-@Xw#k-Z zZWY)06P~r_!8#6Uwxr;k)%KVD{<+?{SYM@iuKrhIj+n5gkZ6p~BCQ6F=W5#3m)lkL z=uN!t;mIVSp%NDPWSwPq)^CMOp3Ws~Gq#9x7;QS}h<5tYG%hzFeosoq?j>QP3(1Txipi-<>?@|kGNz4vrpa#EOP%Y+_Tw;-{BrOj!b z!wjhfHw8Y5%|%Babe;KfF2d1qPM7DQ-Yl_A6SgGGSoY02H8919 zHA?Gqrs|(xEWhKoecSZdN6I#MvL5G$mezn`MadaorhlpC5v=G9?{MQ&&@fndbl=hB zWjm#B89uyN63w38|E+J{-AY>@!O%lW!fzjNF^J3+^4_w1hwveTQ?tbU6P?onoS96V zo!C!2(A}fzF0{+%Rre<+j|Rr8Hx@fvedpL#!*w1GBbqn`gY1M7xIIvJNF@ujMC&b3`!>c2rduGXgwz>O1+v0xnj*Tjl zz0wz{oSCY#y+C#4!g(*Jy<6KFX%bo%slUFlkEKCk({YxoUlP}xIa|6wMQ20C2AvJ= zmMa=`^2GQKdy8dEhrD_An|)7L(O35zm0j!k~C;mYf8kGF62Jsc>ZQotf2 zF2>C7bI5AXuYWI&^%ie4xvVS^vETr2{^eCKU%ql=X_#p>y z)@bH9CFsP`j6`AoCwu?Q{{8Jd-({=5&mVWbVe`q->TofNmyl-mO!W|%T$f9qU(-<7(9B)3;Xq4x&HT1DmImdJRWmm@FEB`r`0gjo$TaC!#3P-Ug@wu= z7d*dFxRFt%>-9ZBd*8=z?fzf*%6|U)1YZvZp6#z6NIhdV%QTP>Upv`wO7R<8>sh|r zW}f->gz1eCS2yFN*cDGIjTuB37**DWG)#|u5N;k}X%sBWk)|*qM6b!<+3D17XZ!al z=e9q;{BO^LC)@l#@9Ju05qw)LxKMmmxWi$G3H*E&VSDZNJPTTW@jAbXi^CS9`g?-@ zbIhmq-pUedS72s6lqjQ^m(+btq$~gK{jlTT-+c@^(6Nxg@qnBX@8Y7J+fFZIZgBG1 zX4O%a&Ar*{Dwl|Z>%4|G*;JuRQ_lq`ESPtCVv+g8PX>Lb|812hP-a$T(Y(>?Guf%D z%PFy{lk>GhkK)0ZS;4}07l?)D*YBv?f9~)0@ALQE$er%DRzSypDNmEg0XNoDYK#UA z9IXr!ZuEEweev?`Ik0Bh<({3t_q2=J=1NcUNJwbr5%c(C^mgys<2O?N&E4wD*{iob zbKiBdb$^3)UeVEe6k6fr9=@~iz=IV#9&+e(r$5|&WA*y-6#4x^yh1xxBn34~RH!pb ztvK6qsqx^=Q}3=>D$Dq)XX)GHd$uLsJGcG0_x_B;o6F2f*rYihc}lF_*g4ThVKSE%XTba)5`ib21OhlG zUOB`4{Q5ItC!2+FEHN@3&Oarc;`$RFEz!v8IM8-v)!X#g^IIm@R9xA=x5S94g|lI@ znOn$Ksik2mE(a1W7Uld+c%{oXQAu%&2Y2W~4Z+A2p>e7Ul(vZFcl|qEoBdtLcukAr z;~jsBmfgQ__L143e)L5`QL|6Mo z0MAqoCWjd=Cag*UToV*n6K04!Ua`YS!uqT2hM=HbKYvWPS#(i{!^-D_0Q2sWeeXlE zbQdx$SZ(xkP0VWjL}rmq3A$SsMX^g9a1W2T1sf!(3S-f>ErPSpWmK8XvEMmG^vn68AoR`)iob6JQPfSA&HHMsWRPoiC zrcoZr&bio<;c?O&9yNWD$9t<^USd%Wb`VbDXb)B6UV1<1Xvr4!_BFMidhcGD+29gz z&C6Qz&QuYFMGYE`rh*JgiH8glh18aWzFr|}d33e%%$A7MzIA11V`i^E*}^6>C*hFx ztyR}96)kY+oUqqNRsQO=0~*_|{XDVt`z;w+Z!Z&pgpZsX(-IF|V9@y*<8apR^YSw{ z_m}+oxZ!?$>$QEtmS2j3qK(*uEcC1{35!oGaVlCU7UOcs;z!}30F9u@tIHNHnceni zA5z@WyYe zt_?dvSH`>$O}rX0Ris{iN273zQ>V5|l;?is37d2sYzou(D{e1!x@fiA;=-q8ZO;_E zMSCXmOC9}u>`K9zCht@^LndVtd&O3xHdfXZir@OSCC=2_p1DUkRE1eA%Ynrq;N8lJ zTANmB%~^P2|Ix~_?6W(YME6xpjd*@{!EVQRvk=Yn#Xg}7iP29}SmRU-qWY%1SaR|2 zi8XV)SCzA~9lK{F)t-9V(V=kv><hN1xpOU?yB=(7He6(U zGdNQBnQXUJWUtO_acPZ5Q`lrU82w7KnmD;{3uu|l;Z1+}Tr^$w@KswmZ<)wlXBnJk zwQSRCSfHdaDPtSE((AKguf$I{Fo-EQGVm}tYHv$mNN!lXMu4Zcad%X-o~r~4XH$7> zq|yp?(OjE=;--vy&R^`#)}*R<&nUdZw87a{skpU?hlhjh#!r=oCvEXoiL;nQMHi}^ zF$t1O+quy?BYkt$W^LVH(R}-_9#H)*#F@X5H+c^8hStMtW=}6q=j)bZ3(2y56XdAG z6XCnOVrkHu1G-yR-)_r~t$Hh`JI8``aq{V#3xZZN9MUjYAYh=u&>*sG*Y#bkeL}vB z5|>t{y}mQ~*vnrBC)U~W7;?*US-jlz{`u>U^8B~D&t`nMYI4z3b&bQ_U0;_b1)RLW zEnfC9gvWUfhlEYwj_1Zdrzps3+*%#H6y0mP2L~Zlo_tcdv3Y^$Et@*i8dQkRt6IJk99&D6J_15R;BMIABrjbZgMUy|f3?Bf!V!!Wgj-ijk-U=#lFOofF$h=8vQB{+qhyuex1*HcJiVO{m z431q~zfL}6FTWZwS1t3+VI{qXb#gg0x{n?^I*nQJ4V&9}-IU>sgG-F0mPqvz`df|Ii+jT?Pr7Yv36alR^EAFJm)q7&_qLYE z<;dU8lUss!h8qsvE<8VlO%j5&wjAlCVOp=+Jr9Z3fO@H|Myqy;|Y)@cstlZtC)zeih zvW8*j+Yes%qOKMc9hTDWpXIkhYs&#o8LxsBEeG9r9d0mK&cCxG(slF0DG_t4!cToQ znRBH$>ZFpm^>NXzUBxO)VV7?23wm4Ba3yigKaP{v9~s@S&#F4#QX`d7w(I+bR7OtY z1r4bWOpK+&uVUB5H)S^IbxG(OR={vfIKVu7|}g3nW-ozGWjo|+kUh9_l5dbcBA z#kB=H*kTzn6V%=QNO}qwObE&iKi%_tuW9RzzW4qGJ`&ZN%uU1NHzmA!VxD#QdciJ( zDJN#~^0Vm+zP@iSBCIZ~rWDa3!p3q|=1!ONV(mQf>Ia-GRf02KTsP~veXTEYUD<*; zAt?<3UQ4&F@R_+}%7qW>PyY?SUa(X4&!*!~y=z|Fjxb+8cPXz!q@ETSMW#xJwNxZFq+DL4kgy=Zal!$Pysm>g3-6s=Dy+rO&%%&1Idk#mf*nTP zHygDzPOsT9_39xG8O9gVE@9@j2Inx>V5x5}ZhYLg zyF(%B@MYb)x9(p)wa+>&H<^3V-97WR$|*^{HnHPX;!-)^Yx~LJ-At8NXE^pv((hjR z_~Nm1cOLG$Z@=$|ziFsWPyE+a4$^x$7kKckirc2NhV}aKXFqFq+lIVu-M&!v-TM7D zN1ls3e|_wZqT-J!Q%-N#p_o*<#gMbvgPEKfLv{}7RY%VLqVNnNh^O_Q?`;jYCD|Ibvr}W>|%xViu^zzbV*RxgNSTysE zMn})>4Qu%hJMaFMoGH`(@!92{;_LULPrqx~!~5`BSVp6<)GwV9fdx+5 zLVi0W<}`1d+^MM|886j+V}9J+Yfi@(RI2}3@idENkLc7rjsHZW53=2FxG=Fq`fR@oZ_-b;^vO}fX|M+aDt9*MBy~VAepz>8yP~+^Yx>vu|t~hMt zb@F6?)Vyiu`^ukGw}&qezo=U~S$$RM_kbEtwGB=4r!N1pGh_1NkGZF(znI5ze5Y;v zheeww>}USCT=C8ZrL!i7dmUFT=37x#{p0Dkhb6U_Pn`bwDf;if?7zRv|LgTs|NoQz z`}5xVkZ4Y?)wkMKFLOE9)ZuU=V@*ZD)>X;9cefvZTxI>su9wl%RpZ8I3GU}ze}#9Q z6cT39%-R{o(YZt*p=s;2*Fo2}tnb(V^YG|l)!vx>&wpy~uUq~3@ZEFE&wu@_-tH}S zHp=^n#n=AuxJNM-TUu>5GS4<(bV%Ivz3NC^cJ#%aZ!Dv?`Bzs|l-Fw?f6M#$c;%Hy zwzZONswNE!64+A~uk^TXbq^8Nq+9d$Q7cO_(F*(a?Bp@+gH`6f27G))QG@Woh>#qdVmy<>H?=4pqI#&3Oj zx7c%&8~<3w?9nmr=esM~xhuW*S+8e% zaq*b*y4_J0Vl}5OZMDzNa@BI^D+^hj^-${Ft*3?6*X{PozdkzG{qK*O{q=Uwzc$@I z|L521KVO%}+w#Rfo^F48KL5W*8s~eA4{uVFx)cy_{Fu}FjGgBmKfe3$qgwT>Q!!Ic z3KzUT_9b-byS?HizPG)@7kW)$aX516Lex$tW0CcbKStI6D*5##=FYXx?)z%Lewugs z;LD5alQ>d%L<52O?% z@@`INH~93Y_4LaJn>2VOriesMa%}P_Xuj#-!(O#-Z(VcPy^`HEf4?68S5miQpY@#k zHC6x0cI~y>UuWXBx>?j~qF0h@zC-Wwd0l%}ooPS*)713Y?QE76tWy{|daq28*ZTe{ zU$yScZ@ZN*LOa?lIG26(+cv?-Cx-8+;Fmsi^(QeoN)3(6PoA@oVEbfoxZ{`tFaZ+xix^?bc(MBZJ=301y-L_bu{zUb4@PVp0JJ)IeavULr}tH@t@vz`EQHU8()dvKV9ng^=V4{xqm;;oS(Z}{;yrt zhpz8+PhTF(-q58Haw5`fUhn&3KNnO#Ijwy^t*_^O!e9QXS(!^GOkLW)F6Q{_-Iw3~ z-Ec4LOUeJD|9|$({ob;5>z0J_FRa)9%kTdiw13?$-;ZB5{Qv3y=W^^k#(RrbpLXC4 zyB2NG7d!XZ?c4tx&Y{l(?~iq>yFw@~2*!<6s5Dn}=*xhnnc^S5hTucyveZ8p|Awf&07Vxjk*DUI=OU;?W>Y6=uSM&3yUAX@bUpLhS?v%KG#>yy>q%)RBZ_UqcR`}gJl=DjFhm8ci2%I_6?_r94! zYsRjqqYS=ayUjKArvl*VNCvYlBak1i7}} zj6CwxY+wE3*SG8Dtu~g^tGRnql0~6UV)CKP@Ar15TWt!vcm1(zOh_o})5U?@Q_PAC z=cScB-FLNOz5ef_m2>>g+fJ5R>!WSXCtAJRX7kE?@#x#Xx_B0^6`Yc@e!Krq6aMq> z`+na({`zTJv|pe1{_4BcS@N$+D(bUWuX}&}1UHMqB>A%Z4F&nHo`h;I zj?FuE{%qd!)BHVGW_3mI=(JbvyKgUhFYeCqv^fXLx3lVO4OqY}=PRL~{?S-+uCqbZ z-PkyuQrmT@d%~ARGDqCpe!0Syd#6nP`StdC`{S}_=1)J9`lX;oTg2frC)eeOUH3lZ zO_?Taeb6YS;h@O(?icSo{5;M|WgUCtc-z8mvueFSgYTBvzx(dB+~d2v(Rcs5stxJ; z&sSUb?c6G6DtPTpfI`%TMTS2woUZz|`)|eS`26+fcfS2?@#k0QL+)*}oZcR)Sh#;% z|DEM={P+1@b0{ABv{QCcci;1q(>rI&+T7w~i2wia{l9--7w_NmJ9GB`r}n?k{{OLE zeqVi!)T+z>ZWi)a=`Z*5Hd{6|YaZKz&QmMbUECwRzOE{DzP-FnM*Q`!@9qA6``W($ z->2vM_x|7Tgi9oM?*xmg6`PGUn@y^!_Wb#^-TvN^-H&sw>RVY`Ijp=~c{@YdHJ$7C zmss)o?OAEtE-vO%-9F{U#Mz%8tNyo;$v5wh&%a;4xBma>`*#1{ne5%S%kTF)I~iw- z&nJH-+?(mY>#bl|l8I=5kgU+f7i*q8_lcLAk^KDhG4X4cv*#%b{XX?lsQa_wyz&-lh2-Owc`EF(?*YzpWHrHuT>$T6eKHDe5m(_hi#PQ ztKSRvd$zrO`t8$a*?C&OR&5XZo;dH<+c~$au77>{^=XUzL))I<^WEE}V*G_lclFhU z+1c}cYpaz0|NsB||Ns8qtgkw2e=X+p)vB!$onl=IN3NAfZFKT-(VMy8#Yc z8P2YIIqwZiH>1~;!`nn||FqMt|M~Luo$V&Exfi}&*uKDE3a3g`l7G(whILo&c~wun zqql$Gx4#y#@qZrK+de*o!5P7eC>{bVOA<9 zw%>kj`sm9C`Tu|ZEuX%9_pepIzop$s*PRsHEZTT~ljFkq@{UJ1mOhWUYI@m9{`JYq zpVud^KdK-9|C7;tY4-vR&b7<-s?Ga$*0s9zdSzv{C{+>{`UO+xsRvs&bGbdF3XcRDZ5}+OkI%8 zpSywYJAa=E?$A(4shPkcEWqI5Q}69o@Y(I2WopIrDFyU*x}^rr9E;!i^j*F*S~$ISWj60W*DDj-!(7I&3$Hj``$S%m!EHT-v4Put?j?# z_1o)zhyMRLCI0m~->8g<6U(#pgf*9}=@H0fjLh>lX41JSW@NA2zx&0n*Aj2q%uc+k z+}G!q%zW;yS&`UmjR=p9nqxO@p03+FF=kar;|ed;hp+p$d!&cAbca<~l~nI2ndr|R zV<(X3A-q&W(c`q{ISz3V0r8xTcAx&dp1%Lj+xWk~&*#4_Jo$QG&ho0Ht4@pw8>SUa ze&cC&Uajla`hP!OZ@YTgMoazck}X@FZ@ad-xSPc#(f7rY8|;(39_;WvU7owNm_NG1 zLi6{#UcYraO?C$SnQLER{BuE9_r!=>BG08x?0$TGdw%`@{j)z_e)#M{*7ntx+Vl_9b|A)2x?TwKYHKx#r}<@%bNc#v|h=eEo9QBCTqIt zUk6LWS)q(STR$KF`OD&WtbF-vGhQQ3*ZC|HWMeXWpWljndg09}sYJ{7`Ny}PU#p!EvtuU(G)8banqwR=bOCH<>X z=u9qR?dYCYuYdo_mNh;5cJ7gt*W^4BocUfQBYmcg^*P?}N346AeyMFpxb*kqwJ)C> z%q5tWl)J)jtxjg+j&nH4vA=u4)QM{57uw!AB*)tSylwyU?aTT9zV&{6yyhoE`yyVS zzQY;wr7Ia6L>4e{Y8rH~cxf$Aj`lk9_0RG7k3KGp$vb1s+3J;`EVNE$U+yWc28Eny z%k`)4U-5Or>WikD%lEw(+RXJnR%TvUg7&3Hkt-V(^i5VVJj40Bt+u>8Cwy$KA8-C4rV{N%ApmO`|{;K9z8*4S{WdT`( zA2rNzaCvxK?9p}S^~SF*J`R3V`Ru>#FZ-Q0{{3H*G%@0{yr#6mB9$XHXP(SBxmM|- zi-ynMNEJO3p+k3K=R~+LF=zxhOUO@L)XF74K|UagQDMO;?;lg8_f*(9Y_)IrAz>eS z?v8Dhd5T%YYNkZY#efG@NEQ%~Kt@bkb9W zX4$h7yJt?jD4BDM{Y8Smd}EJ}(DxZahhAt2*Vso`1q7b9?0NY2L80p@6=639?h9V; z1a3<)WxsY{@l?y_+^KeHVQyeYN7I6!8!O+NTmIp#F!acr-W5BC7@L>eTCrl*(#Zv>FGAQwUL`cSFzPIvr1a^a`0Yh3D)ZUyMxWezaA#Zh z_XkW5o*0{Hq;JwplWBfuIcYIt_wNOnAp#K-HL9y~JlLzvEyQhtbfmvOT%&)USvTK2 z?DB#e245D{riT6NNRL^f?CqwrOk(cqIlpoqZTz-9yv}A%&f>KXMILOO%ffg1dQYO; zmd`w;+m@YnQ)d&Ox8t;3-0OW`Rb5mVG*oA(E)-KpV$TfLmH9TIIo8;5pXZa~zR%-s zh~=LuZ}`L;b3Hfx{Jq#sd5VfI90y|*xf~d#xF>wegnf>e8PdjtX6$CV73ewDqJd4Emcn*0s32=Jd+ktoY%+;QWIk5lSrb)+dha z5ZmSup{l>{qS~n^-j0DsXJ}q(PKb55!Qk}5Xwv8HkG@4mJW6O2QS$j>z?G21xRuq^ zq=ipgh$m*t1B)54sYP@6G~ZgXJLi0>>2ae6H8P~vcggdwjGo;w%*Y4Y?1=^cZOxG%nmPA;(}G*&obTE8tiju z!a_|EOTl%?jqWOJ76;hWq%K`_o8lqBn5ost5XH&GGXEa4luFmZm!H?||JB1JAiebC zksWtGw?C>q_xL*VM0o=T?Prh9tbC}v)^e5Bx=S`UO`DaZjhc*m<FW6Gu;)&+~&fZbUb< z@JzL>S-i+2O=LlP28$=}6@}|YI3na^brQdnJ=5x{9?dyvp(yiX{dLRCJxcjS$1A~U#>ZHo!#WT+Ly{p=p zTzXT7pX1oXUtfCoo?lD)WTn2~k7o0Nk1OAMRXa)Rq$r||%`)gE)E*;lLIL|-pem%RJzz%3)*)22Cr^UaK3U0hYDa{fWK z@9XDz1);0&?v!$w=Bbf+;o;ry@e;Gy9Ge>73!K_l%<02w^fZt`P(@HjWD<|IHLsTv zhx#(3;AST-PvLMu9rd|J2U~7EsqEP0ryF%Hlsh5Shk<#*rIwRh4vTGwtu>U`;NobO z963v+sl>w3S;b{#!lX@pmzWiMnt~XWxGse`EtWZa_cdF?l2z&(1JY|aPug@TEx53t zNs;f$EFF_0Zg-nDF1C;sgh3TpGNpB)liYm{g$m+d)rTg2J-_KgTsrXBh_;S0PV{RL!G%(nGR?n+R zTpW1h%17tF`_q5j=l{R=6aS9YD%T`(G9vDaM+taH8GgTePk7qqyw5B4ESkOKW+Qvt zuXn%peko{;FEM|Ap-gn&7yr-MHykrxzFqiGuGGS_{*3k6J7;GsJjSy--*#W83IpRN z7mM9a<<$%0pR=vD3opN&R<2{2d*9~CmHtJ$W%OR3`0yd>&#vivY?kdm!&}~Y#k646 z!mAa5Th>Xhm;9loel7b#)%(h|FQ09>WB%``L$b}K`zGSbDgV|b^a`{oG02~s{y%M- zV$KPshaL)5#ThEbDWBFjD7ZL;Sfwz13S?NzvXAfX+q}>ek<&Y6dITI?7!-OnjwD#L zH**C(l2qmEZQY=`a)(1}XxPu@oDG@{x!pNg0y%6>ZjMpGwEVy{k_49VY^{1?^GISekY36>>8SIn9u}F4LLVn%r#`ju&)1UwQyUmXOtBgm2 z$16=W6AznJ22PVc*BUagt$$;(@x`<_ySN31K2MfRVQlSuqgB$a-&XHkGtd5Tx?J{xi}jy&Jc}zj z-97XCv@EF$hAxa80V`%4)(zF~39qQ>iCp%|&z()A$L#tVm#4QY1;2M6+qV38=wo-q z*Q+YF-F==v{doJz`e*O=g}z_Ce%(F(ysb@o_L;}N6n;MXGf2=o^hD_PEJv;*e8YT&I*cEAY%YE~clQSImy6M<$52$myT(c%}9BiX}4y6uv(cFEWxwyFAbO(IN$I0f`qqf6JcV-?i`T>nR3_y$d20AHDdu=X_n( zn%Q4-uR8i@Z)Ek7Y?APB3k$Z1KU(m1lK4?kE|&+~=6*6hPGL4`&qS;uFUvBv_dj4y zl~6dqCzr!~L|OHj(!`ic|%Iq)Sj@?>$zG#V(WUTJQild&>zVq#T{?fS?sD7WV{yO-{LMtFAZE$?-ccdH0pslV{n>gVn$3AW`gS1miS>2{&sZ2<>`e*5=h&zTAj51)^P$Gz`1?X=jRuwq4-SntZmZj1TsjZdho7MOE<$I@S^Za$2f zeoGr947oa23aTggu9{nN{b#_Hk1m@8#61O#Is;QSW{7r8`J$Pt!qU2mbI&h7D^3Ll zh1>fU2dlk1Yc8w1b#m?WrJEE&I5aQ&tj+2TTCyYH_{J76~9n1Qf`*ysvu3fS{ zVh7uD_pU1wINq(@bk1jH!i-}tjGno)`JdXRC@d^=yxDu!&jnkI&T%K*zu4Ad(k{}W zydpVpVrb^J#{swAFj))uFgQ3&cyv*x`nHLk{X7qo{rkJv&(w${xp%AHRaqYywywz} zBv@Tqd*(EkfccAeecsUEd$Y`}?5o~0{nY(-YZphY*_C4voYKqGwW7OqnxLunVnNXo zKgEr*_qkgq1Q*x8t?n!Tsl~~pBHb`KW!c87pKt&8d3>Rf^85I!a!0q%=v)~;VP~VP zNx{-3)?Ob1{c^s2xc^`6@AUmEb5vb86c`rt{OP`bp#Eca=sknkYdUsktja#hG?_JK zjZ4{Asc=4~7D*w6JLd6Ef8=iGTd?8i%GTWM1&cyr&&AGnVs$$?EmchDVN_vek{DBK zNMMTw$APk?x)ao7v+eu(Q@G+)#Uk_<)s~-$lQ7v39cB92XR6ZqjNhNXdf$J(J$^!h zxUC^W^VHXsbzSjUJ<_$_H#o~4`!2is=DBaeZNBU^_x0=NeNWzJUvvBPdBK3DHfABd zi3%FU7k+F!p)Adpm~?Wz!fmVi2gVcFV_299ibV9?%Cs5&_VCm<-N0dvu%8Sy& z#_hp^JPi|W?078AyM4#vMG2oPO%8E+Fq|)p4X>Fz?b_Dnx*0zTOOM~L`d!odOm?Br z)tsk`zk91q*9z(7U|)SmhPk<=p{whW%)OGcg?G*8++LKoB{o3WM}?7rrL~Kz`K#v^bp^WBX%QvYXGpML$Vb>_cY*T2TiH~zcpZOJ>G(#aRMJ8qYGt+98_ z%FmKAbIts}wwc6C)|nol;Nc<_p`ci@amG=u|0%cEFQ0f`$8BNx2GJ==;cg$aQtQ_j zi+fZoPhl@S81wMyO^f~I_W$44*qeQRjs8Rvg9g@~#8o^;n9t9-eeKx7l#AX(c+>U-_|i4Ro{CCS)4Q0qUW=CzHZlSv_5#<6$G^^FgYIIq0E zY@g|M?ve$!R&dxwt-OCq>bIfX+1G!TNO2!jowx0Dy6I283Eu(~jl^w^M$5jw{q{#z z*9pzIRsESU`qP(hl|T8j?B@sN`fDEb)!%E~**Om!XXjWD$>>(GgT?MjtjFhPe*9H& z>rdSLVLVr&u=BX5k1TU*Tg>{14GVr8X^Zt_53OmEn8_BM!ueZ8cUzl!+N&KpP9k*= z+;%)#*JJTOCF$;I1BV?tk8Xy%xWk!}CB43PRgI--uGz1r>DS(F{T0%@t*m*=CIOkd z6|Z$>1#pX(>PfBMc_%+?^)tuEf7b1Jy!C^ItvfS=#H_B(JD>e{Vsf8-+xOx-M~`=A z_2t}{l-reWP87N9T4f+QzoWkA2v@!SBAYM=y~l~XT~K+Qg~82lqNiOm3+i%!Muujh0gaw?iU*`95#+)Y~Nksa{T$b_3zaCL^LLM1l?#_ zdeC8m>JCjC26MAF50xw5#TmEgNXkBUS(WG8784tqB-$6WWWu3Pi*PNYE@lOj)SQ1) zV=Jq_e!O=x_an!;=0jqxN=r{XdnLXuQe7n?QSN!l-tYW%wI~11`1R&__R6V~*e^ak zwsGIv>!wrV`itvl-JbdC(zMG@e~RcA3h`aF*d(iMA7!0o!r+k9V*OQW%i04En%F&r z_RNX6E~C7!;B<4DQ)QP$ft#$;e38LsL%nt%!+Yy)Fdcou z&eSQh;8bVR({vxVD@B`KxDGzD`o1b$MoF)?a^7Owt{AmtDU(GjGdtv;?DT!EdVbZt z8awyxYi8?CGdlF<)r~UWjJccc{@M9bJXp6>H*I>1`(=isu>$Y6^0xo#TzmP()6)-@ zz3xnSe}6-nq}idDvzajHQPbuup0;4>TTmrf7v9`{q&ZS#;x6Ryc%2XSKPVPf6ji zRz)Z>I4|Se(DbdmFV$J}@X@fX2hN>R(E6VFqBP4mW5&5vvaN?rOgGrNHQ!8K8S_tuQAFv zvEI_ndU0P4SC*|nOk|C3g}DFCuNzl=|M-0S<;SNJ|LpmDeZF;*cW@Jn+6vQ{C8tiV z-f+uk_FjeKZAL5m7H#C!mhrA!bE~x?W{$dn+lTl+RX?8ZZ+v=PVU~B0154!I>6Sm+ z_ap^PxN=2+{gjG=i|;1RbqnV2`5)Q9FzEnawEUFC!mggCJ6+duuD>iAppezc(deeh zaXx8+=z5J$6^B_KtvSRf!l)L7(BT6 zGw$D?vTx6CO{&xn?aE`@65*{g{q7x_()_*uTILkn|2@pjo0SC5Y-sx^C7i0& zIP2)uxNhSs2A7+MtfMe9#Jc*NTn6Ekr|h2>8ZmKEhY>N>lj!W64@9z1i~I%(GS%~5Mfxt)F4tQ z`DwP>lLa~MS0?&;9yPn6->{HlqRW?p+Dnyw{JS6jzP?hoU){`sed|I!8#7@67g^=_ zJ(EqRT+&%8|ARGif!7Jo#mpMNf6v`HNzcpLH24J5f{VYDUl&a^l;xh5$~Apym#tn) z>TSloDQ9xy5_k1Zf8pxq`m`Y{Fiq)B*s}ZP2mkKl+@qeL)pRfI65rZuOQX`ZuF18T zJb9macGdsxPnYWJ?f-r=zB5Dg@v&!mHf$|xO_x2?v1rrlz0N9_q@ntJlBI=WlFI4m z(CbG9yfP*?osR664${hh+?KqVAvHb!szRY;?3ENAyQh-g*?%t|dcGughVdl^L963I zl@&2lH(j+_`^)4j{EPK;*0l3?zjA(<{xu(WViRq0N`NNS5pW!q@LT_Bu*-ChJG-;PF~B zSAda4^U~QU6~6xES#zh)md>2oX}9+7W%2r*(V5IKearTiC2y==IlCpmWvv>s(Bhsf zh8w^5k4&_Q4P?xX5aOMb9D2&>ksVz@Uf3(zyT|d=kH#O+7!i(lpl8?78U=ibS zXiyM5v*%;o{*B8SSQ2dGSR&`XB%N_UZlq75CTgmM>jBdH>N| z0Y3o+_a5H!OsA*axK=hLyhMZ}itkNmOhRFUn18OXhpRhNX`38}r|)%HJ63y2Jl~W1`0uX|zkYuDWARtvWx^8O zzmKi#&jmIwU2}J`{RG1rRmH%A4G9fP>ZK}UQqArbz73Hwn!tRjsBgBB_!-Ac$D4N+ zMZ}9ZeZp;tDyGWZ{<3_nW?$0x3ixV%L=!1ugmtC&V7MzH4xrcS^cX&7_ zG0~vhJX1f~nibQN^GPCUwG<8)w zWz9AxFj8ex7ndZ%mUj+c_5@kGy?uLS@7axec$%x{dN|Kf{`W*A{8Q-vve)+)`Yr7G zFZ%F(!~WRQ_j$tf-TD9`<8g?iEgl&wbg}HuSq0LcvIt!ua$T9rVX`?>hf4FOhWN0vK3{?GO1;;OqI zaN+gD^tErVezU7|THCk$`SY)T_uYywKd$d^!=k9Gqb#d;PRU{C&H9@9qB{=FgpdefsTN&m-in zE%z*c{BZueO40psi3!p39la(f?J)Q6n$kF5rB-IezK?nDY8!aOmhU!pxN7jkA!*Cw zo4uP)PCh01_}-%ro-zg}3@5NWt^DORc<|I3-(-`xU5Sp^ihn0JXkj4Vh#A14uW>$$*| zS$mf@NUwbM^YCQx%MUyDc>R90>}ra%`0n`frz;QE<(j`QyV)c7Jbl#}bnsKh3F|lOwlr*WVTYPFg%&Wp30_ zwWUCg=buye_L8o)-BEXWB}?tkvHo6>9lZX@DZA%?CTULDE$6W_(!uZ%$2sovywin$ zi7M~Bz{l9KqEBY}SMCQpa+J-&fA{M@T$Otf7_#qzpr0D>+J2Hm!kLNE}oDs(xCWq@r-}R zmg|3;{f<3>BZIUSNIk@S@5^AuiAh+*M6zdWHpNm@hYpWvKQsgUw!5K z)$gj2PlFh4_%atf-IZ)i1L&~6cMbPUpEUo-RS z&SUdG*zd8e4=extcQ?0Ij?kG$3%bqkooGG2_2AaXh@XFo;=gYUe>=tW*w>Gz?_^GU z87=qD;?9-o92eJihQnp|KI}O3c4mwR4_ma;;%EC0b;oBuE4$hMw9x-=K-u=V&!b%W4Lo0{Kuo_}^&u;!+`4pAuXbS| z?*!(x6F!Pe;bh-a78Q4Rheh0$dC&hE+49|e|5f_u=`Azc|1Ljr|5{D!9cGn#F*!`e z(noC>n?!$gOjtGJh2-;Nd!B22S#GoM+~;H01>5w~)6PwOCgyCvb|3fFHFxumKbQZm zm+qdUm)50hY<*>2=?>dDRe|>Zuhp*WiuircMUZFn1``|G#y#&tAKL$^ z*I4;nuW|Nw-J)kU2W=kJWGBgS3v6UG`XaNWby@cHDedRp8y4<)dfNQW`qz<}iHV{) z$D9&NdX2bdh35wp!Yo0vVI`We~kT)Z^w$-7d6G)%yziGu>ELbZ55k{3XeLoX^k*< zLWWlF5yx^av&Ryg7R8#$JwluUZpr5&uEg|h+!=gOV!Hc%zwpynTOW2EWN<&f`<2b? z`uYFN{(ikSWsS3uw2YV<_uU@TH_Js5H|IBBKT-Ja-;3KG*XQTlEvbB7@bdG${?n(5 z?uPx9dA;@}PiEW{^TzvCCAYlFJeGYCd!6&w_T%5DXTP}Vt?}>@yKj|UocH(Aw`X5p z&Y7>ro_GIPsmXK}_R{!|QFeCc4@^J3yr}c(+g)Pvv!`cCbUZy0uz7D!rQV(`DG}^( zr=$+QeEo1`xL3@K#SI#v>bL9kKX<&o6EE+7U3%KCvZ8-P z`EvXF4zImtcP_rfl*wVk-plMN9op*~48?h=4cWjU~tyOzov{t@( zf6b@o2iKkOe({O(l*3U+i6Xz-cP}4bl)OZKN=)|k9QAoQM{ll*V6W56I?%iA)ygZ= zt@u^NW<9^MV#?HOTBTRko~yjEc58jk@yvX`>!z{+40|4ypETdH)BlIY=0p_+1{u@u zC5hZON;Brjq?JCeh?}my&{ye1@6i)=7wdRq%M$Nfy*?a%pEZlS@zj3pKvSDH?uY-^ zoG}j1oq0R&>}R*#eo2=?<~h!?nV7!d*QCC^C#GM#tsZ@8%jDBvx9!W7=U2}8`fu<3 zw530~`@>()&OiS_b~EG389P5Nv)J6c@X3QyrV=4vVh&sEe}8}PzSWv}2SuY!s<~9Y zjk}j`6?eTqTBA7PsknK@jC(OWMHO?o-+f=Y%jWLC1?&IalD|Il<(X+q_UzatweH-L zTpr)MSEF~#+gbbVb!AlCnFIY2tO7-xMZR_c4x%usH=fC@t?|px{;?>MM zS$D(t{`~U&_wn^BSH5f&RbesL|6zZiK(<}PJw`*%M#t=6A>{&dZmr{l4zs0?PIa}80`#-+EJoILM%FTfU(SN&eoS2tG8E0bAa8Mp1QYu;*JKlQ{j0Sj+&#j3w7emO7b zbZ&EWZtCa1Mip*vZcb0|vX-cxyP)>t^jX&mUGC3h?`Sy2^T5pHdwTvv;msVzp4YbX zUj6pWFMaWQzpCN`|8n%N@a4t*{wc8GW^Q3_ef`~4LZU`%7No3PeSOmL$LkVzCQhDp z-7racifERsnY{kusuK~f-QS&%zFy(OvP^?j!0A-jsoNhO?|ina|7+R21s}hB`S$ar z%^j1thq3E`04qyHpW%~Q|#()0;HlMd> z@0=j=EtX$fzfbbQ3bynv=?2F2D-Ib>n`|?S>+Rc1_tx4fQ55O^o1ag zUxjQoXJ7BxynD0#kDr_5q@FwMD3bnq<%?Fo8UK?UEk@BU>j&yfQ!+&ymNDI)KJSx( zk8$nmx-Wj$Ufh1uytzii(QS5+7F)H3Uxr4g@<+}3Z@T^8Pi|O|{chc_%eRbQZMk;i z!js(VZx^qw+PrkzstMWJ>zP=^|4IDll?<-0K3;#*A+_2!=U@8L`eiegKfhhDJ2w9B zrTE?r$uE*5SRD?`=05QLO~vh?M3okURqsw!)Z8lgee<8b%-er?o%w%kG^1`^G6`u| z)&KB#UQ%ECg!I$DcRhLY^XNArj(@}D;p!%?4K(Y{h!@_ug%;)pZM=@Ev=GT^nF{N)*?p6fSlP6 z(i(k~op*l;-|~mAY1Pex*Br!Tf|f7at~Zze|E2QzdGnudZ;xaCRuCM@9XO#_cXL5Y z#KE%+B9pnb7Idy$<1l&ezq9kIH(kAMwaw{8>uVz!`7KLpjoGG19g{q^DnxhH(n)5I zYf}Zf`x7`P-wF*#m-p7&SMT>X;og}U(l2HSH6-k?)jKUTso~|VS2un>%)WiqYS+VS zN3~nlvT45X2;BJ~ZP&RlFU{$OTnrOKVxCL-M5gWdDcGYcvuoZw``ta^(|y~5ZCr0g zSI)cD#reOli+L-DYMi&M^ywAsB}ZodvaH{C(_@;YdNi*YxjJscy&DZf?a;~Dsl7a^Y*=->t*qHQ}jNo_8qIvz5euT zlg<4Kp-WSrHT9aF|IB`zZDQ)FYbBAPE6*rBxa;-IG&}C&XWeVTr#D$1JaS|End`~x z?moW!<7LcVH@S*i;tb3)r8`&|TKO#u$_#HUU$=PS)q=#8hkTzjYb3=q#H^n`_vfd3 zZwzlIG9O!9^vfi5g0H~B)`=2_dIkA)Qdlm0Q;zAmc5U~Zx2K=X+I?8QD%56E`1Nbhb%^Rl`)ad7U?-q~EGJ-2#OxXWU$p-|DcJ=bP{L$?Ki( z-rQZtz@)jklIN5%`;4S-Ps=Z=&xoz7-qqTYz4TZFuhnfI%k|rn>n-DxCG9WjIkYew zI;(lK?s@f1gBKo=N9J;uKezdQA${7$C+4f&BHq{5zdOJD?vI?nQrTC}t@a62<%Vxf zJe6rV-$d0iVdwAV`KEF4Z{KgKe7W;o<+)eBY`1qZJ~V7d*P6j;H}`Q+v_;~rrzf|D z@34`4V)QqL>27k}&$9Z``X4{PeOM#a)X>DX!F7jgxLwe($*-1XOFhnY)8H#SFvrd= zLc;j@EM<@Fdk@T*^g?E?|cc6_XlZe&SG?t4^N~{+|-S~qU*6KM+A1?k@BB!T& zu64zwu+U;Zb6KHpo;IOr*WWy}N?-dS_mSPB6??P3T1^$%^L_g3uePPW#obRNOf2{k zk`FJfo?-T8x=#btXLuE1*cWMz;4TGqyEx0&L&L> zTIXbDZGF1!bKDA*omYZhuaokbs2=*UuuQFL*)8OEI_T_W)f=9ukF2@e*wYv!HHGE)p zxZtmKRH^G<&t~S1OTCWMm>fEoqB=cXtWP?mZuW^(6^kh|=gv75#HyMiI4NS)<@0>` z&K`*~+mw43C0|`(C%<`pZqU*=V}}V7@3tOu^6GoAh+AsL?*xt$`&0iCj>OwtFWVD$_l%9rlA_ZB zQ%r?&i@Jj!w%%6b-m=k1c5$@H^EJ{c(>U(`s8|1e=iFw6l`lH0I_H}BGAoxXn|Dhq zIyUNRPxT|VYkKxue|Dv4n+I`gCwu$GzWe=HV@3jRb;~Lv1tG_0&!djrO3?4!I>qUP z6#yxAK88KR92$<;r^u=Q+<>c ziQHloR&aF4wG`l;a&ej2iS&sR8FUT^xtW@{+;QGrU8GtG49Z^NWfl zw*9TM)oEY3{MDSYHj?`7h@T*W=BqYK?7-8#2$xM9LM))n+hwybo?X z9OLJ}bl+5pWsm%!hrCSEGj6EuU~h|Sy7|@4WHm?M;Z>8Cr&rIL@zIXO__)O9)BkS# zt}MTK%%r$cSA%&e6Nfd|sXcmio2O08?qR6TOyHZZ<7=#6`+i&S(V(n8-NG5Nn-ssb zZZ6&QjVtFRgVG%nb*2Cp2Bw|HY{wFI<#IFg|Nn0E+uC^j#b%iqJUJpE3JQX{PnATL z6}g=^SSB5QkabbP_6v=R6f`xJT1(U-)qGyI%Nv+EFSqsP4RTq$$t{KBpyIC79cQ=} zCvBSRw>Qn8XXkkj#mP%pbQu`9ckK<%<`wT_ILmh3MV#lMR#JV(fO z-u|MZb>8P-vj;niw(esr4(@V1GIi>`)bJOYHzzf>FLj-@(s11+UKh8+o@W;9;IICq z6Q`S)W3r#AP2>JXHT(8JizG%)1|fE}yqZtR1sV6-cLs!Lc5Jk|)zh6CuNtM}*V{OO z?dUlLy{UC2=C3w&xjgv8xj-(k{{HH}3E@vBZM6K%T-tV4ZQkM6UePL*E6kQlom}<0 z@XL;sOaT*Dv8b}7{NPRu`2A2)lzq*o_i<_b@huD$iy2}&*O0O~z+xv?d8wzvVb7gx`9@xG zAEj(xDj$|vTGTJJ~Zp>4B^G^GdA0HE~ zW>t2EbiI$~<~eT3X10VWY}>we;SV2QoLyfnYs1WUbX%aJM_UumB5pwj7A9k#sV5xQ zSIfq-N%D!9&RnS${b-88B|j&jcOAMbPS02*W6P*0aqW$9v`|0KH_3z}f(iz`ijyzw z5qo>#?2bkaEr#`$KYpEPf1q|!RA^=f2ZPrhara}oOPLfI-gbz1o_tfPH0Q5WWv_&& zJBz>m`~TQ)s`_D_8bd*kFYFkZk2Tvm0eFTED?McRZd2MRfC%Rm8S+?8ixngv}no3rR}$qJUIS0VyloK1KBI5)uZ$V~&expzZP4mdGc948Es6&-mhj$yNoryB6o)SeCSd zsX6Z2h9zeNRI+6f)BVnNUODBkFQL_4`RI|>2b1=%SSZ2JBjh{h>4GOs4t%Zb1xwW@N|S6Vo5DXk;#|p$qiLTTPsF*oJMD1DvP}rFyqJG=avsC%5KYM> zMLvH0csr~6y>j2fm<^5GMYa08BfKw`KMcxsT)5=xyYC6LHy6}or^vs0miElg(X+7q zQik|4$3}&4g(TEOVia;^HZ-N}hhmSky) zYMgNrTb1N%c+;@U=;5T3D;g%Kd|#t}?uyroEPf{8XfF1SriJ2j%qpk5KRS9hzeYrC zQ%X>*M|9g#sp_tUDR%|WHRQkV)0f|5qaYjYBQ!nME7INSC!fMRhqQ|))zU;fEDz@M zo|KzqJiGRrUc}-F>z^N+GyUnKHcgKo(;iW?B8?eCX^(HEjrlv}8RI`M*UPE+eUulOaWVz=oUL_RLQH1qrp^~t*~o!PkJ zNJH5aqmmlEZc87_I**AfAZ(EXM477&%1VtWnsmP zQxnUcFP|pq#4^3;dJEIc$&bDXRUVdQVMyfZ?hU=Hz|q)nU_(hQ{8nr5<;G>vm`oxHH>&p;LTjL8UEQ!bFLt z7jFbJnkE=LTvVm7DMMwaI8&8@`;G|${1fIBKU`e?tM<66*X@|yHzXB#oWvwMCiy2e zdRBzy3tUsMYFBLgNt1xC*=f(E1BKMk+l9(CT{GouxHth z%a`O@qJH@u(;4}^n&WjgZmPh^S zxY8IX(d59z%st`262YFC$zHYR51%rTQe4!=8Oby!=@AnbgRt{tk2;Q%UfE(2kyf>; zHeovF`b07vi=G9|4S2gLHC2>Ns{H7_4;oC2R$ew$54_A`mFo{UZE2V&IU{T1R}o++08BqiEmf-&*VT_VP(M2L3-3Be}?}m*J6)Hs{433a=Sf{a*a$6kBFZ zaroyfIqvoUvZ9|o{t-UEL!$Vglw8fxRd+pSs_x2|!Eor&oJFhMFA8*XUC=abdvmm* zy0WIGcI(nyFW2OduVDfQ_VjH1=34ZW%gFJ2A3BT0x_A z#)*>!@!rk;XO8-0>&{`XYP(Ugpfxv>_4eHA1G3LTf>%AhRk&E& zY6qjv6hkf_rUeS`MDrzJ` zA8M2mZ;_uOuesvv>KxACQyZgwCo0c4Grec^sw?^Be-`INrK-2D@~+CdK9MhI>xHTV zJJ!r7oX~yZMrT8O__S}*ZJz_5e7K&Xy4LmjRW=QUJBFOs>&xe+JNQg82`+rFOMv0& zj9VY-q&qe#=(8VO%Ho-G+(OY9$5BSQ)3Qe#%{$OtVi$isOroKFm~A5+5F1Ko0XAA^yRh8wT%Ib4oN9G zrmP5x;*W^#Y>UnwYLT@EB(#qIOU;k3S*9vo%PIRBBt99Lj<-3Z=y{GSww$4<^ zVw@MK(>2@Xsg|~C{PM4w{x?^Bx5_I?nW7YaXqC=O`+)dqU9**ww?8%7u37f=cHD`S zNcZ_}Ha$uFYMU0YSZI9oyLUp_d4|>fOQmJIxo-t$2QpbqR$sd@urRk-;!fxTky*dR z7l-);O)<$jJ0Wk+B9$6O2B#n^AuYixuFQh{7D60LPAfJnNwmBx?v``D@zm4w5H^iN z7ONK4s!glZ@SC6O7q7f9FWLWi-5rVD$@%@0j`}FvDpSiVIMue!%W+Qev32{nc6^JI zDm~`ulqjThB|t|xmBUF?k43Fd&^&Vnn}L#&bD=XcdEj~pFc3~tP97GHnnWSFMfRRKlqXohv-% z`JRn>(JkRyan^5Y#v)Z`#)Ay}OUo<2#pU-;?&OxR{<0|3_qFKBZ^1rql-5txiTdH(X-pZi{RzCC$A`FZJ81L1AQbx)snHepz#!r(ZC zh3TWB00R??UZ>yR%fHV2ue)=|sM#e*=*JXx55*(SHLVH^jV?A1oP-z{T$q}IR!;6{ z|6!mYH^p*wcu$Yl#wGrhy(fc9(@alR9)2IAC&H(As*OwiZgibxS#aiY?Z^L?-#5#@ z`1Q(ULsP1Be;A;58g_Vk@j(!pA48jcE`YRac2r%hB*Q3xuUdFEJ4 z%0&;a(@w`WGf0#lR}JAy@d+1QarKKzjF4~S zMUVFE?Ny5J(tj8HKJffe%sK9wdz+Ve&U$!r_qC-jw%ragJLJ8bNxCol@oFQTu34h$ zd(sYW*Z;KgKQC(*t40p*f|pILD|PuzDx@))0I&$N$o zj>)ar?8o?hYp?GN*I?CY+Iwb}ThH5D_wHNg&fC6QUF8;a3YjpbI0!Znv@t!0^*r;yFV25&@4k17-q(fG z>*E(+>)W%de(y^0JE`&a{@8!%4tjig`ql5vS}HT8{0l-;l{L-;+*$i(n_(XRm7SLs zPIXpl>FrYGU||qpTfB0bQfYyJP{*w{VfnfTKX{HjpTB3%vm$L)ML{8lty4ORxSMl4 z(@r!^yQH#byUNY>>9-%wkGm{v=f1{eyTk#8H(^4S@1AZvaAEoWpKEuWZ$Er?#q1=V zeKYEc-k)0&d7|`~irh!9u&Xl9T#u|<5GK!S`KH4B)7$s|PiuW{eY|2 zD-#6S+xECxzu9q0daKo%&9jx+v#Q=(C_Q(t`tC*xAFP(YQ3*6JD2CpRIpF^d?T&uT_&HIT3+0$ zlk#om-Z|^;b{g*#&r^8VyKBzO`DuMu4|{$$_k8`L_Ue`io!!sd@7$0{_t0q;d2lt0 z>ty{l>$j=fSrr5n9-UkJJmaFt#XHMV-p2iXCmg40T6&uGqs;cHrF#!AUFy)f;B>|{ z*`IHpAG*5JGGkifAs?+Xo~}2y<<-xAeR1Yl_sj2&XmWB19p%-Ea8l0Z(OSgeVqQ`4 zaryku553o4-uCLp8UOz);^J*<&;MdbxFoZn<%-uPwX2`_ouno=hppMv2XIP zrA{|qzB9k{EG^)vjO_7e$xP)1rjs?ZSIKO@Xloer@mtOL4OiQr-+Jq_*{{7*&#i6K zNj|n!jkj}-9l6}Rqr)qGZF-(xea!tk`)=N^xFz4RO-rHW(5l;P*S?B~UsjxXxhm&s z@n8SD`{q7<`KdjSXZ{Rt-=in<{qI=izFYm)#cI>?UCH&A;-22vbfik6S?2oHa?6|L zzb7v(t9_MtW8&ONE067dmh)bC_o|9Wf4}t?->27BJh3Z!oThzrS7OSk)j9dq*Hh2h z+z-F+`8>HuseG}@Ntu$Z@|%_42XX(+C{J&`*JfAY_xIwK!q(R^n$z?7o~!l>^4C3b zWJ^?QVqjr0@!&n~q^TvAa&lR?#l3A!kzO0R4~L5gGfwFEwQfP1Rrs;zvMmcPq&(}L z(q(h0SL*oVfcGo0R%k>9L#)!4Fq=j_Ob za>p({Ul$#}{pYpp#~TDE@t*K$=5I>A_IznwT%}ZdkJIrfkL7x<2*|xT^Zv%l|21zl z+n0a;{MX|D+(-GREtaqSv~8sW!~MOCKmPYjZMqoiUN1Y> zefG+^S3*M+WGnQQLn|!Sl*aokvaftU^>WRRmH&I$|2`@>>9IAGbC)X1(X(@ES<`hr zT^MEw%np$>xTaAjv9-lPNmH7m^EF2jV`Grc)uaDXR+;iY^^aDZG%>SR&#yvT*4yp7 z-$M* z>CdPhHDk^te-mCZa&b5^%#-n9?5JEQvsdz6rAn@5^sZd#SH8!ucUE@>Fn9>4Tv2pU zn96hpR2uv_Z2#)xte@wm+yA<4FOc0)^LO&&o9)xz%>Vnx_;T-M^JV=W>|4u46BY|g z%(!&J)_QgS`Pa|uxBNSN|4y55{Jy#We*N!VUqAb_`_s3P)A#=jv%ju>doo`{s8*9x zR%a5+%BKffePz8Q9t7sD{aj*I?-0N1^V?th=E~O^`mc7>>sxLABkPpq^22}r?bzqH zul%Tl6W4*`?fJ|e(fOrnub&FZum2HqO!83f#|Y1hPh9_f^}SzJzIWo`;{RWMT$lf; zcinmW+*2#pwC()yI^dJNT7);lX(q+a5UH|16;qG@m-?T6_pjmSpL0&ze82ne+n4tL zAGSX~{`$6keY~7`ZjMCr&6}5Q7DS!sHQ01f=Jey8W&V26vDFXPUN6^g;Od|CZb#9M zClzylf1a}Q%!9g@Q@S12E6$wKdgalj*V65&r8g`7Uu{3W|Mu10dvZ(HP7Tp0c`Po@ zwPxokEv+`TtEQj*4&KkdK572Co0nIg_O%vZU^(-llbeO1fyIOO^h`CO0}2adR(}a? zHuroS74W7o-}A6kSpL!}hfRJ;WcHlc>5yE+bE4yI*OL!L^NU|8{;hA3zzM7kNpvfk=+%28RpV9%1WRWa%lyzP)(i_RmKvzprn3nWntXe{X%p-npNay6-l8C9E5@YQZZ9 zl}Qn|u6(NacCJ|c^?w=t_4>7Bd+8^ic z(7OEV5|h9T1znBnlj0v9{}iLOMxZ@o^{y+A7FHK?&6>y(D0RkJk+H)d?%Rvf*7*DN zd@E~i=Qu7&oKT>9eWUo>!|^p3$Cj$J8bD z`^i)5C-O|@IpE8ZJweEaB{0y{+_!4lRZfk}mp?E0@4erhx9L~=yWL-A?7q&n+pYfE zo9n;tp54w`|Dp6v?Y|9wWq$wvHu>%JtNC`nSU@{-MaPpH(S<>dqi?q8i2 z=*-o`z*KwWef{ec%c9yh4@2jjIBzp&^^#>%Q#s2ymtD*+myh4KZTay!>)1(`kDoq$ z`}65N@2w4Z{Lb^zkF)8_fLhbZES9I)s3hfdpFK4XK-WTVGMuMwf2^o z{JFS#Gr2kHy{{Ik|NS@f_Ibbj-m?5xzml%J4A7Oj!+p8KL&dkPRZ6`$IqW{f8B4t=G)nr2<5pAjZWN~x{_F}@5b5RyQg3Cd+p3^g@1BGfBV+{oom_g z@#(ScbK1lIO|Pg@;XlrNDC4TW_mr-X?_MZ{Ze7BYujMks?fmjL#=paqM5q+=JYv5D`nPy<&oL@}f0cUOC!N65cT zwQnR6H!q7ne>HbSN_F^Zw}M9Y=1XcS`(~6+mz?b>w9-b@Klw=C{#P{(2j1+BTKM3U zslaNmnddqG{`~sdYCCyzf41}kTF3X9_f7wFJNZSacWjvPw|`HM|6FhX zq-NhXzb%3?dGdAU1rtQ|W_E64n|7}4>)Xmb+a&wrzs~&lW8VLL^X|M|`fXLI@2XGk z5;l^Y+@~$G$?5txP-|cJ@|F$}Ic9#P}D^SC|BDnZCbf=O6!{ z>BFAKzZzzwf?1d!E0(ea*j5j~DLCHJKIk z@8#W>x8?u-{1#{Q|Mu;2i+jPj4qQ7m7I8u%oLO6Q&Qn*dn9;=s6y^pjbsJ0$w5m#+1>L#&9{FegQnm;wfEmd)+D5aE^d;w z`q%vS$AdW`%Oll;4H*qt6haobhQD8?$SB+S$!l^Qi+Qc+zhC9MSMTOdRpCGN^W57n zJI>0VpMLx4*SfPw1wL!Ud@n66n(bM#)N*IU^}CrhpiP|xf zQEk;ugC4!L9SG*J3?BPLS(7`Q--R@vPdw_2yB+rV~RN zELlH4*A)LVO<~pbAiGcH_stAn|46j7pR+>mT=jn$ub7O5XN*|Vav0J#c-%ex_~&td z```aQ-(U0dQoNks{XYwSe&V(H-!N|d7`_$`q*pNc~2bWnk|KvJbTM%c_gO6V9^QoHLn{&8E(2h z(mM6|=0&a)sXIv*XC%AqJ{qCdGQ+W`qvJ*MNwtS;jtnVtRL+L5F&;|E+_d16rIACH zaH00ipud--mhYPOoJ+G!)zkC&Wl`5uhO~&M8&zEl84k3#URl_}Y5X`xa$>HK zHvL~kXa6s={-1wO*X-W1^_t2)u z|6YpPS0CT58dhA(b9(K(_1mw<*VO<2W){bCl5OVeGeucvf+~ak^`^FUd^u((&wqae7-!})6hqtMVZmDQO>oaNlA#W z#pfXhgDaOL0|yt=glG>Xu7?6k*j{Tev>2$Xh`2d1Ff9mRa!_DuHE@|2kh;jB0`Thy1f4FnQc$vv{vvwT$8w^vv}W0l`f^e z`w_mL#cChcs!VB0OqdnOAph=wrbc1mCYk7~O-V0KX5Nysd~@d0&)5DncWiX}tgR(i zaYs(|(VFLDTq^pGnR(vnu!4kZ--BP|WXI%v)qnWE;+su?a6$Ds*Y&&sSw=l))HSF0 zwCY=b|HRRG#?vK);h~cT(_ICSbDN7NWFr%gLbC9ZJK>ji`9Ck$&$n-{eEzSd`fpjCjc(<-7mq?B&y;pu3|(m4 zGJD44B$*ez!Yn6P!&ZK7n{dSEZ`So?-ZM7HdJ1@YR&y}f9av9e`mEtb!^}C?EglW z(rn+Ko|eHPo$z_>yO1rF3Yy_wT&;137}jMT=>7FduPD(_xJyARsj$-N)neuSvpi1S zTY0fVX2HfyPS+=Ty>Ak@*>tFteFHP6fcFo}f0ij%=hf^fd;Fww${kk@4hBhuSXLj| zFUw4;s$yfhHqPeY4i-JVX9er|!_g~a{}}tK&lGiIaP?}7WzjT#$gi!fUU_e-e}UEf zl>5QcziWm|AI#y%n3UlVv-OZl0e8v-?*kXQ1de%d7GJUdGfzdBvtxIr)EOp)z@_5jz16)t=1hVrEALjcw&=+&}2r4&`I_K6E|{?KqU!uHIU znQeiMF-LuG9I~9@uEyBHz|i!W=jenagLM+tKV+Ly?ROvGTMml$eEwFp>i83Z>y_*7xiD?ZW#(G>S7Any-@5C3uXWr`a=qMe z+%tp0PKuLFRZ-D_$FXPiLWMHkrFm%!t13(yEiXrYYGA7fY1nWgOkty(!bi)xmn^G< z7E3Q;m~pH`(nWmRM*k(kbB(`>+&JPDrrMbF^h5^>M_Y7pe@Q~?B-X>TKgfuz>}+HS zs*uf8_X{`Tco?%P`F5#Od)+~Svx2PE4G*ur6wom9yzpQ`lg7)XD#Ah>D<__4R_eU! zV-~#Aldsm}rRIaJ3uJ!&{kLULgs&%$gY`m>#HKFa(1VA$R~`t-UlTZeRl?$9r|zDq zeLCmdvup{GdtGen(zP{SyjaS{HvRfs4OPYtp6BjM_dog*_{6a~;@8$-aVTX82_)?B36UsECH;1r<>P~u6p}P8gXzD2~#fkP-|5w%i#ZH&tCtEb_y0^QEFo&jYQFUs$o&Wz-f3^4lO}+&z%1w`o zc0IJ=+8N7{-lsG5>wyW$Tes^hy}-xQaVq__ZOk(NzwJ5GciO0NOi4J%q_HFaOR@IJ z6Zb5ASQ=KQv39LFGO_)n+@JrwuCEHjOYex>nRJh{-F4dRkFU;cvAeQ9Nuuav(8Ra> z3tw*U{rJ4jfWzV3^fl0gRa_cuUB<@R{MVN#3VBe&OL- zpUmK0QC?fOC$1|I;gmRu`x6*h&Ks~C7Jo0`v*JXyrTW^5;jJGFTf(w(%C78*d2f93f|8b` zps5wJPnfuBca+7|{FkyPA}eB=ce-yDp1Wehsas6lt2ehrKRl7u_b~g?r_2Wp?;q;! z^IErkiBIIi^`V`5(h~wz&V8GgtGG`45#!#6ij8@OnxZXBvVxpE^Cagt@$W1>RQ!Q~ z&&Po^enBViXXCXA7o3~7I+j0}$uQ@G(@GDqr{+(7tAE{G;$A&-$%5cDi}s}M{(R?C z+_uP4QI2UM3@l5QJ+s~)GxM+a$J_jKqXPR`bXV=U^J2&SGrNz8)HN8|3oqZ~+4txi z%ceZ#)TzuGxeh{E+$+UT>^hrz+bZ!@!AxndQqfkW)yC`2EqYk|I`^J4%ZxK-qMn^U zZt+}ObX;~))~3Z;Uwv1dIpUX#eJ$;M7U znB-J`iG1+ln-L)NBv;hstHILD!w1h@ZtHq2%X4dek@uy;Kbe~nTs8mhIesDI?Hjg| z1y0);8ocxm#yYKVj!Jk`H^JU8UxpwW|g`&*NMAIi|sE z)zuik=z!TXpQtrzZN5`wx-!b-<>!>wcXI!)nzL(Vm`36h@kvi+83?S>auPE7e(T&q zp5LWU;wsl&ynb$XX^xrt9dtFtC)^|Q{0z<$=m6GFmOLC`%tqH%PxN*+8g_FEH9FDUFUp4a))i}7} z)v`~&_pbfFZBE(k`IihNva_~wHgKyHu344R$vdefYDbci{}luM-#`DfKhB^3%h++E zal`?seLwHiNh~Os6*E05Kx4;APOesq?O#N*g&A9%0^gT4C^5eOq5kyk%cr7Nf4@xG zalm%7rq+Ff(`_+=ldRS*aO(Z&!py*NL(14W=%C882X+_v*c3cgZt8vgW$B&Dy6CTE zE%6Ur*p`Gn)U6T?ICWMx==aLG1(L5d?y#Kemsa7C-yD)%Imyqhaq{WUA#03PE+kk- zxgBV5JO5_k*{z}{-gO+=H?7%g3frQlcLziT7=*6nS#YW9MMMM`{4FaXqMsJ_grRUBoHV8%?S=7#Lw~#^b#}EF=lkao>Y-^YPY?sQ- z@c+TPT87)I(eH_*DBZQ#sde` zy*}jK)k}VUZNjY3{i`?~PjWmzz!I?Ouzb#o{oZcp{`$4|Yos{1>={2!(1-E-3r|6bRfmPG=o!Ar?dd2M#&(3Y-oxM}$mipAo zHuL)Oj4i8PF5fxVd(GiJUn@2+U*@uE@=##;xA@clx1KVr{~6BTTmE%6(|=!){5s_a zNqia#-Ny=A?*E(~x%(&M zp|^$Vc29n^HSpi5*>KlW=taEpEH`d}^(d{q)?zP+N zv)S^Fy`9y~?YS~C=et&Ur@y=PcI(Wb82PxD>obEJ^fS$^-srl8uRT?8%)|D8QM&0= z!%1uAUH1%`=6r0|Lhi&lTZB$O-7xPK^!+u}q8=EBC`?3<{J)4Dcw(Ifad9&PpE5lgu z*qCv`3>L=ei*2~Y4j)g^JE%D)t3urD^V*FX+q>n}BewQjTdya^C$Q1?^{)**?N#y& z0$E+}H!W{p(pEEV>GegqpT7OC-R8W_Y}whgd_k3*ht*ToE}wPQR_mjPw!lBTw?*;$ z`_exbTc4SJ|IhQk;^t;~tLs`>0=&4qei+KKTd$aY-0KIE(L}|9sXL#zavd{L(#tIW zW)VC=Q|$POnmKBr6DOE?u(E7mkq+Zc%xcWs5WRIn(^Urz;ZR$pa{}(1p+^n>9#}qY zYkFVk(~EptoRyx{otwp$Wqs1EfI-W3;>zZF{@Oi9pM@pX@9(^_jq%>yJNMqt)hylp z)K}nx-~9TIYVr0;cRq>Fz0BIZMEE!3snp{1&i3EGuB}L|-tvlVy`H#)$f;Awm+$^y zv^{QK89q^Vs+Xv!zh&NVS(Wud!4$JS>o?ps3C?;RBIU%u_^ zt0Fn}B!SPS$v)86nz9~OlHeB=D=s6|#-J2`hx5Z8F zKj|#VwruMvwPz~~xy%D+D(z?s*to5G!r9Jqj$H{Zee7{;ISz8O**YIsa&a8Hxvb__ zUF7CtZH-p^k5x`A?NM2}dTz?wiAS~h7HG> zW(%;HZ`^%RYR~hR?5+u2N*>G{)B2T@>K0Two@7~JbD%ayr!js*x#8nEx6C-UKHvKx zXlEb;qfbIt^p8s&NuH@Zt!pM+lK5Tmr?htdI{w|iENboM|Ni`~Qh{gs2N!>dmeYnW zI}{dw66X7MZT7Sy)6cq{s@nST+GqXhmv>*b-mcxd+tIg`Z<(VfW9vC5W?9i&KH_o< z|Lvb%S+t??3p@K8HqAF8%ci`o+jB_f`D3Pe;hPPD+f=5;3TaJWxmBf2Sa63&u}S1< zBcE?`n)Q=!&hlE-GB5ar|Lvde?x;_T4YaI@)_8kGrm;6Q-796Xv(ee=sNZ7kum5d% zI`RC*^8W>2^~K8)G{cmSxGVEA-rIIWP+(j6E(eDL$Ck}1y`X=JrL8HUVE6hv*6fT7 z3Y`tSMf09**jYM>rS;F%-(UXS$T}{ddiBqp4_~5xZT^yZf#LYm=P%Q~|MuR!KWo~l z8w*<8V~&`vXlnnuc>(9jZC$6f?ytF8|JO>l^|6h~hO7GaHJ`s-K5aBVk^5guXIaEW zu5SugHRO(n?rG%y@*{nE;Df%H{Ckx@?dRK;zb=SfWwttuwLnO@zsL9wtN6wE#P`vj zt4@~ih#ja5%n4C&D7rcK-Ja`K*FED;+|q76b!ycr5vLqQ&c%-nPBBcBv^s8Nb!V34 z&M&jRpRBH4qJ8Z{X5q)mx3k)OG&eal2)GolR!rJ$BD>!cZ)(5)?2-Kb7yudQY|Ib_ZxOFxE>V7}oeyf&e)q*|MO@H6lktK7Idt

oD>?u%E~w(N=dTBLYTL(SS@=ghq6#eaA% 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 F006Xh-QNHJ literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/images/studio-effect-composer.webp b/doc/qtdesignstudio/images/studio-effect-composer.webp new file mode 100644 index 0000000000000000000000000000000000000000..0729b5962166959bd9b9e66655573d27eb414926 GIT binary patch literal 81872 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%0D^EGSIo&D;9x-+xH|EJ^q1-_os^)Dye1 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 + \uicontrol {Exposed Custom Properties}, select or clear the + \uicontrol timeRunning checkbox. + diff --git a/doc/qtdesignstudio/src/views/studio-qtinsight.qdoc b/doc/qtdesignstudio/src/views/studio-qtinsight.qdoc index ddcced3afe6..6af9b071e17 100644 --- a/doc/qtdesignstudio/src/views/studio-qtinsight.qdoc +++ b/doc/qtdesignstudio/src/views/studio-qtinsight.qdoc @@ -4,7 +4,7 @@ /*! \page studio-qt-insight.html \previouspage studio-texture-editor.html - \nextpage creator-project-managing-workspaces.html + \nextpage qtquick-effect-composer-view.html \title Qt Insight diff --git a/doc/qtdesignstudio/src/views/studio-workspaces.qdoc b/doc/qtdesignstudio/src/views/studio-workspaces.qdoc index 2b6d2e2e27a..407513c40f6 100644 --- a/doc/qtdesignstudio/src/views/studio-workspaces.qdoc +++ b/doc/qtdesignstudio/src/views/studio-workspaces.qdoc @@ -3,7 +3,7 @@ /*! \page creator-project-managing-workspaces.html - \previouspage studio-qt-insight.html + \previouspage qtquick-effect-composer-view.html \nextpage creator-project-managing-sessions.html \title Managing Workspaces From c673e6aa201d019813935821413d3ac6d264fe1b Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Thu, 8 Feb 2024 16:45:43 +0200 Subject: [PATCH 029/176] QmlDesigner: Update extended View3D icons Change-Id: If1aac862a3e62e518d321e61ddf3e7c25e30efb7 Reviewed-by: Mahmoud Badri --- .../images/extended-view3d-16px.png | Bin 254 -> 311 bytes .../images/extended-view3d-24px.png | Bin 305 -> 384 bytes .../images/extended-view3d-24px@2x.png | Bin 455 -> 674 bytes 3 files changed, 0 insertions(+), 0 deletions(-) diff --git a/src/plugins/qmldesigner/qtquickplugin/images/extended-view3d-16px.png b/src/plugins/qmldesigner/qtquickplugin/images/extended-view3d-16px.png index 81e4de13679e6973e3e77a866eeaa3b044e9fa52..d9027813d0516ac77fa72e7c6f5e78116ccd8132 100644 GIT binary patch delta 295 zcmeyzxSeT&WIY=L1B3kM|A`C?4F5b`978O6doLP#IXjB9KGgrjbul9-L!BdQp@QKK z_UPuTIfq$ybi6rW);!hPBUQHfpp%KlH75aS*W`yB4=4J3I@~B}QT#43FV#kFwtG(@ zueImzj0Ym-t|uc7vhhupy5W24P>^Qz<_^Po4vuTI zf3G+ouy|2Q-C<$5$s79_+Z0>YXTD_B3%bRiuh=r5^>wq?^2?To(^o|u<(d}gnCJRh zLTtyB|IISnpKhHZsd_dg&cQlU?Wyjc;@&cjh1`b6^v+$3+TzIlwX?eR*5erFjAbV; z%t<}PwqW|Xy^+ge4x~JNs}OU5WA%>{ExwD}zQ?~!v1R*xU}G%<0|SGntDnm{r-UW| DH>!lF delta 237 zcmdna^pA0ZWIZzj1A~Sxe=q|BgK>aQh${mF!}|5>moHzwYSpUcV6bAvij^x@E?>TU z#flZnmoHzra^(tC@Wb!g9|i^nfs!DfzKhNv65=~TQA*dCGY zw8BbWf$>Pl*Ei-aRo$_rE@Dc*&fL)6T+-OLV|v_ydw=;0YZeFzp2&P3c8Dost!m7J c*;DLg_0v{yEU^}H2l<4-)78&qol`;+0Ae_0c>n+a diff --git a/src/plugins/qmldesigner/qtquickplugin/images/extended-view3d-24px.png b/src/plugins/qmldesigner/qtquickplugin/images/extended-view3d-24px.png index 70cd99bfe15d2557d749f64ce2b9182ada420d6e..a8a0bf65a4652596c8b2c268c59e413d520b5e87 100644 GIT binary patch delta 368 zcmdnU)WAGJvYw5Bf#E^9?GFY9Mt4sa#}JRstyAs&S{y{$?Rmt)W@IdF4H1)?ZrkL( zpnB=;+i!U9C|B^r9T&7Xl(a@iL^wHhI3#|M88h-Z6`tUXZc4wX|OG`hmXe1q?fKvJXTp zSe1X^Wx#}!>=R~1IbTea|H1zuMS=g>tgaK)wTsRQ)r9TRdL8JpFV|`%_q^-{TRT$B z7MLB>J(|5t>60nTH4*hLrTUx|icybJA~T&ycI^VQG67Ma(oaiRp_ze!fx*+&&t;ucLK6U<2%GBw delta 289 zcmZo*-pDjTvYwfNf#Lefv!@ss7>omaLR=Xb7}l>}zkK=fRjXDl2ZI$WR;*mPa{2P* zD_5>u0S3#LF9*|5BK4|NG6MsHKuM5aFoS@CfkQ$;eMA3*`3v^nfB%2>qErS3hPj?D zjv*Ssb1ypb9ai9RxhT&*X^UpsyZT%8dOe{hHf`Q;!N$Rffx$KV55ocH#4~+TU2#8ex|3wUqa{yF9U2a2H0vbRx@)H%IJa&4$~-BBm)l+i-q!UM zcv5P;TTa3L?4r7clGvTUr0x7G_zyI(nJ6=^QQg8M>`)jRe$Cint?6sgX&wK5pVNFM hBlXkK``1U-lpBJ-_q4v~XJBAp@O1TaS?83{1OV)of~Wuh diff --git a/src/plugins/qmldesigner/qtquickplugin/images/extended-view3d-24px@2x.png b/src/plugins/qmldesigner/qtquickplugin/images/extended-view3d-24px@2x.png index c6ea236a02e4153bf936ebc9545e622408124446..a2f7bf93d379323478d8416df623ff89231f2627 100644 GIT binary patch delta 661 zcmX@kyohyzWIY=L14B5|&vpg|rc6&4$B>N1w=)iA2|G$0ufM3#FO`G zx56*ZzQ4Aw@fXV^^$T{6kKfk6kyf#56x4ZiblH{F0#%cuE*w5L>FyHGvh?&#x=;9> z)5`bH{rR^%txP?>n_JKGdP8Y~a{QSb-UW9RZckBJ%c!q$$S$#-FFlz}<*qX88Rq-z zZ~2ulPr7v=dD;9oZw216n<~5%`!Gl8ja7kdihX6J^n{Y>hAC`+6a>OEbxjmrYJKQ< z`Ljx0FUnGs$72_#&Eg*^^O?-2^@sW8GI%Pr)^co-Qk}Kl{(yPHgt;f&9+WCueEvFH zq=ccSaKHVTH#+hqfp#hO^_oq5XWoR*l%AgZK*VAJ$M36W{A#3oG80_hb?*9f_sqYx zH!4#amDLi?zR?J{T^xG#faGJ%3GK42Y}%_U+5BcOu=1UGQzDs=I#tplP3qf)GyhCk zw_QpNt^z9W)6Sv_JW-we*u5VIm`Qn*PBk4 z3-UMmFX=9QIA6+~E1F@F(6P$pEH{oRubh4Ix&zaj**6mspR~-mXm?@y7RO)HK23Ws z;?*#Z`NO2$+uhHG=gc*IkgFtOb0z-B)Oz2RmUe^ug)BE#3+#74pkKV=ddAeuCwueG z1#2)L_Sne1)#$;O%1gTe~DWM4f%{nZ{ delta 440 zcmZ3)dYpNJWIZzj14F_c&aDg#43+^tA+8Jz4C~jgU%q_#s#U9&gTbm*t5&R7v2x|g z<;$0^T)A=u7%X4D984pKg)a`DV_;wqDhcunW)M&?a7f56sBh?>Fn_`R^Y`B$Hi!>q zU|`htba4#vIR5tP#j0in9@YmF>L*?;;X7RUzx}T3)F!qo-EXt*Y`y<;Ew7@$TJG>^ zzR`DHTzcfX{dqjI$S2=`ru|%RVw+#Z&NsgQqtzglL3lR<`-d*`Q2D0wF}YLbG-d4G zD=NR{zq#6S*+uNT3K=}!F!}vmR(*X(!v~q~FWt-prfoiVPM%jFO#0Z=<=fV%{ju$+ zXE)r!z-(0PbAXXSvr6UY);sP2^PV29WjLb~cDzzgz9v9;@}(Wh46`HNgr1D4zt7sh z`P$BorQvABjT4FuTK^8p-LUj{D<~@6oLd&+yg*g=1b_LNE6caAMK;?{@3_iXTEpLQ zYNGZ7Z2?{fMQx8elE-*Gr9C!Uzq>8UyDDxEyJ3fmO4+(Kr>{S}-zf9$$xRROee?Lc hx5<`Nd20M+5S#8Jy;q<|fPsO5!PC{xWt~$(69D_E$#wt$ From 99f5b8a0af32dcd7bab2a15593f91217adbc5a6d Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Thu, 8 Feb 2024 17:03:41 +0200 Subject: [PATCH 030/176] EffectComposer: Clear deleted effect if it is currently open Fixes: QDS-11787 Change-Id: Iecd177a153e88a49476dbea0241c31726d05a6a8 Reviewed-by: Mahmoud Badri --- src/plugins/effectcomposer/effectcomposerview.cpp | 8 +++++++- .../components/assetslibrary/assetslibrarywidget.cpp | 2 ++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/plugins/effectcomposer/effectcomposerview.cpp b/src/plugins/effectcomposer/effectcomposerview.cpp index ab7da90e1d7..e7a879bd8b0 100644 --- a/src/plugins/effectcomposer/effectcomposerview.cpp +++ b/src/plugins/effectcomposer/effectcomposerview.cpp @@ -69,9 +69,15 @@ void EffectComposerView::customNotification([[maybe_unused]] const AbstractView [[maybe_unused]] const QList &nodeList, const QList &data) { - if (identifier == "open_effectcomposer_composition" && data.count() > 0) { + if (data.size() < 1) + return; + + if (identifier == "open_effectcomposer_composition") { const QString compositionPath = data[0].toString(); m_widget->openComposition(compositionPath); + } else if (identifier == "effectcomposer_effects_deleted") { + if (data[0].toStringList().contains(m_widget->effectComposerModel()->currentComposition())) + m_widget->effectComposerModel()->clear(true); } } diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp index 5f33036b40e..908fe7a15b4 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp @@ -334,6 +334,8 @@ void AssetsLibraryWidget::handleDeleteEffects(const QStringList &effectNames) // contain only unworkable states. if (clearStacks) document->clearUndoRedoStacks(); + + m_assetsView->emitCustomNotification("effectcomposer_effects_deleted", {}, {effectNames}); } void AssetsLibraryWidget::invalidateThumbnail(const QString &id) From c584cf67ea8231f27281f93104566a141b5d7e8c Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Wed, 7 Feb 2024 11:39:20 +0200 Subject: [PATCH 031/176] QmlDesigner: Remove potentially excessive values from dataStore records Change-Id: Ib2606fc50662b7724ae2ba11d21264334638bec9 Reviewed-by: Reviewed-by: Miikka Heikkinen Reviewed-by: Qt CI Patch Build Bot --- .../components/collectioneditor/collectiondetails.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp index f1827ee6be6..4acd2f601a6 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp @@ -853,8 +853,13 @@ CollectionDetails CollectionDetails::fromLocalCollection(const QJsonObject &loca if (int columnsCount = result.columns()) { const QJsonArray dataRecords = localCollection.value("data").toArray(); - for (const QJsonValue &dataRecordValue : dataRecords) - result.insertRecords(dataRecordValue.toArray()); + for (const QJsonValue &dataRecordValue : dataRecords) { + QJsonArray dataRecord = dataRecordValue.toArray(); + while (dataRecord.count() > columnsCount) + dataRecord.removeLast(); + + result.insertRecords(dataRecord); + } } } else { setError(CollectionParseError::ColumnsBlockIsNotArray); From 97af12dcb87f744fa898145287edaffa2a125104 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Thu, 25 Jan 2024 11:55:01 +0200 Subject: [PATCH 032/176] QmlDesigner: Tweak the ui of the Model Editor * A SplitView is used instead of GridLayout for the CollectionView * The left margin for the model items are removed Task-number: QDS-11732 Change-Id: Id66171788db1f17583147fc6f16ffd0e69ac56a5 Reviewed-by: Mahmoud Badri Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Reviewed-by: Miikka Heikkinen --- .../CollectionView.qml | 91 ++++++++++++++----- .../ModelSourceItem.qml | 2 +- .../imports/StudioTheme/Values.qml | 2 + 3 files changed, 73 insertions(+), 22 deletions(-) diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml index 6b808d1ab26..c4cf2f7f5e5 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml @@ -45,29 +45,76 @@ Item { message: "" } - GridLayout { - id: grid - readonly property bool isHorizontal: width >= 500 + Rectangle { + // Covers the toolbar color on top to prevent the background + // color for the margin of splitter - columnSpacing: 0 - rowSpacing: 0 + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + height: topToolbar.height + color: topToolbar.color + } + SplitView { + id: splitView + + readonly property bool isHorizontal: splitView.orientation === Qt.Horizontal + + orientation: width >= 500 ? Qt.Horizontal : Qt.Vertical anchors.fill: parent - columns: isHorizontal ? 3 : 1 + + handle: Item { + id: handleDelegate + + property color color: SplitHandle.pressed ? StudioTheme.Values.themeControlOutlineInteraction + : SplitHandle.hovered ? StudioTheme.Values.themeControlOutlineHover + : StudioTheme.Values.themeControlOutline + + implicitWidth: 1 + implicitHeight: 1 + + Rectangle { + id: handleRect + + property real verticalMargin: splitView.isHorizontal ? StudioTheme.Values.splitterMargin : 0 + property real horizontalMargin: splitView.isHorizontal ? 0 : StudioTheme.Values.splitterMargin + + anchors.fill: parent + anchors.topMargin: handleRect.verticalMargin + anchors.bottomMargin: handleRect.verticalMargin + anchors.leftMargin: handleRect.horizontalMargin + anchors.rightMargin: handleRect.horizontalMargin + + color: handleDelegate.color + } + + containmentMask: Item { + x: splitView.isHorizontal ? ((handleDelegate.width - width) / 2) : 0 + y: splitView.isHorizontal ? 0 : ((handleDelegate.height - height) / 2) + height: splitView.isHorizontal ? handleDelegate.height : StudioTheme.Values.borderHover + width: splitView.isHorizontal ? StudioTheme.Values.borderHover : handleDelegate.width + } + } ColumnLayout { id: collectionsSideBar spacing: 0 - Layout.alignment: Qt.AlignTop | Qt.AlignLeft - Layout.minimumWidth: 300 - Layout.fillWidth: !grid.isHorizontal + SplitView.minimumWidth: 200 + SplitView.maximumWidth: 450 + SplitView.minimumHeight: 200 + SplitView.maximumHeight: 400 + SplitView.fillWidth: !splitView.isHorizontal + SplitView.fillHeight: splitView.isHorizontal Rectangle { + id: topToolbar color: StudioTheme.Values.themeToolbarBackground Layout.preferredHeight: StudioTheme.Values.toolbarHeight Layout.fillWidth: true + Layout.alignment: Qt.AlignTop | Qt.AlignHCenter Text { anchors.verticalCenter: parent.verticalCenter @@ -92,11 +139,13 @@ Item { } } - Rectangle { // Model Groups + Item { // Model Groups Layout.fillWidth: true - color: StudioTheme.Values.themeBackgroundColorNormal - Layout.minimumHeight: 150 + Layout.minimumHeight: bottomSpacer.isExpanded ? 150 : 0 + Layout.fillHeight: !bottomSpacer.isExpanded Layout.preferredHeight: sourceListView.contentHeight + Layout.maximumHeight: sourceListView.contentHeight + Layout.alignment: Qt.AlignTop | Qt.AlignHCenter MouseArea { anchors.fill: parent @@ -132,22 +181,22 @@ Item { icon: StudioTheme.Constants.create_medium onClicked: newCollection.open() } - } - Rectangle { // Splitter - Layout.fillWidth: !grid.isHorizontal - Layout.fillHeight: grid.isHorizontal - Layout.minimumWidth: 2 - Layout.minimumHeight: 2 - color: "black" + Item { + id: bottomSpacer + + readonly property bool isExpanded: height > 0 + Layout.minimumWidth: 1 + Layout.fillHeight: true + } } CollectionDetailsView { model: root.collectionDetailsModel backend: root.model sortedModel: root.collectionDetailsSortFilterModel - Layout.fillHeight: true - Layout.fillWidth: true + SplitView.fillHeight: true + SplitView.fillWidth: true } } } diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/ModelSourceItem.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/ModelSourceItem.qml index 2678a36755b..8872bb0d041 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/ModelSourceItem.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/ModelSourceItem.qml @@ -21,7 +21,7 @@ Item { width: parent.width implicitHeight: contentHeight - leftMargin: 6 + leftMargin: 0 model: internalModels clip: true diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml index fadebdd720b..59030cd1264 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml @@ -125,6 +125,7 @@ QtObject { property real controlLabelGap: 5 property real controlGap: 5 // TODO different name + property real splitterMargin: 5 property real twoControlColumnGap: values.controlLabelGap + values.controlLabelWidth + values.controlGap @@ -350,6 +351,7 @@ QtObject { property color themeControlOutline: Theme.color(Theme.DScontrolOutline) property color themeControlOutlineInteraction: Theme.color(Theme.DScontrolOutlineInteraction) property color themeControlOutlineDisabled: Theme.color(Theme.DScontrolOutlineDisabled) + property color themeControlOutlineHover: Theme.color(Theme.DScontrolOutline_topToolbarHover) // Panels & Panes property color themeBackgroundColorNormal: Theme.color(Theme.DSBackgroundColorNormal) From 9fedced0ac93ca5657e6a6925618a0ffe81ba551 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Fri, 9 Feb 2024 18:43:32 +0200 Subject: [PATCH 033/176] QmlDesigner: Reset table cell sizes after resetting CollectionDetails Fixes: QDS-11743 Change-Id: I7b8f0a83d796bd8094981e12032e6f0b465e2508 Reviewed-by: Mahmoud Badri Reviewed-by: Qt CI Patch Build Bot --- .../collectionEditorQmlSource/CollectionDetailsView.qml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml index 037f42fe638..bbfcbebdbd2 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml @@ -357,6 +357,15 @@ Rectangle { onTriggered: root.model.removeRows(root.model.selectedRow, 1) } } + + Connections { + target: tableView.model + + function onModelReset() { + tableView.clearColumnWidths() + tableView.clearRowHeights() + } + } } HoverHandler { id: hoverHandler } From dccd828f1fd47afcece1166e9532e73354e09f4c Mon Sep 17 00:00:00 2001 From: Teea Poldsam Date: Fri, 9 Feb 2024 12:40:18 +0200 Subject: [PATCH 034/176] Doc: Update keyboard shortcuts Created separate page for QDS. Reviewed and updated shortcut list. Fixes: QDS-9499 Change-Id: I27d7dac386570c81958b8af5da2987143162aab4 Reviewed-by: Mats Honkamaa --- .../howto/creator-keyboard-preferences.qdoc | 137 ++++++ doc/qtcreator/src/howto/creator-sessions.qdoc | 3 +- .../creator-keyboard-shortcuts.qdoc | 227 +-------- .../src/qtdesignstudio-projects.qdoc | 2 +- .../qtdesignstudio-keyboard-shortcuts.qdoc | 465 ++++++++++++++++++ 5 files changed, 608 insertions(+), 226 deletions(-) create mode 100644 doc/qtcreator/src/howto/creator-keyboard-preferences.qdoc rename doc/qtcreator/src/{howto => overview/creator-only}/creator-keyboard-shortcuts.qdoc (76%) create mode 100644 doc/qtdesignstudio/src/reference/qtdesignstudio-keyboard-shortcuts.qdoc diff --git a/doc/qtcreator/src/howto/creator-keyboard-preferences.qdoc b/doc/qtcreator/src/howto/creator-keyboard-preferences.qdoc new file mode 100644 index 00000000000..ef59bdf6d2f --- /dev/null +++ b/doc/qtcreator/src/howto/creator-keyboard-preferences.qdoc @@ -0,0 +1,137 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +// ********************************************************************** +// NOTE: the sections are not ordered by their logical order to avoid +// reshuffling the file each time the index order changes (i.e., often). +// Run the fixnavi.pl script to adjust the links to the index order. +// ********************************************************************** + +/*! + \page creator-how-to-assign-keyboard-shortcuts.html + \if defined(qtdesignstudio) + \previouspage qtdesignstudio-keyboard-shortcuts.html + \nextpage studio-projects.html + \else + \previouspage creator-how-tos.html + + \endif + + \ingroup creator-how-to-ui + + \title Assign keyboard shortcuts + + If your favorite keyboard shortcut is missing, you can add it. Also, you can + assign your own keyboard shortcuts for functions that you can easily perform + with a mouse, and that therefore do not appear in menus or have default + keyboard shortcuts. For example, selecting and deleting words or lines in an + editor. + + To change an existing keyboard shortcut or assign a new one: + + \list 1 + + \li Select \preferences > \uicontrol Environment > \uicontrol Keyboard. + \image qtcreator-keyboard-shortcuts.png {Keyboard preferences} + + \li Select a command from the list. + + \li In the \uicontrol{Key Sequence} field, you have the following + options: + + \list + + \li Enter the shortcut key you want to associate with the + selected command. + + \li Record a key sequence. + + \endlist + + \li To assign multiple keyboard shortcuts to a function, select + \uicontrol Add, and enter or record an additional key combination. + + \li To revert to the default shortcut, select \uicontrol Reset. + + \endlist + + \section1 Record key sequences + + \list 1 + \li Select \uicontrol Record. + \li Press the keys to use as the keyboard shortcut. + \li Select \uicontrol {Stop Recording} when you are done. + \endlist + + \sa {Keyboard Shortcuts}, {Find keyboard shortcuts}, + {Import and export keyboard shortcuts} +*/ + +/*! + \page creator-how-to-find-keyboard-shortcuts.html + \if defined(qtdesignstudio) + \previouspage qtdesignstudio-keyboard-shortcuts.html + \nextpage studio-projects.html + \else + \previouspage creator-how-tos.html + + \endif + + \ingroup creator-how-to-ui + + \title Find keyboard shortcuts + + \QC has many useful keyboard shortcuts. You can see the keyboard shortcut for + a menu command in the menu or the tooltip for a button or in the keyboard + preferences. + + To look up keyboard shortcuts: + + \list 1 + \li Select \preferences > \uicontrol Environment > \uicontrol Keyboard. + \image qtcreator-keyboard-shortcuts.png {Keyboard preferences} + \li Start typing the name of a function or shortcut in the + \uicontrol Filter field. + \endlist + + You can change the existing keyboard shortcuts or import and export them. + + \sa {Keyboard Shortcuts}, {Assign keyboard shortcuts}, + {Import and export keyboard shortcuts} +*/ + +/*! + \page creator-how-to-change-keyboard-shortcuts.html + \if defined(qtdesignstudio) + \previouspage qtdesignstudio-keyboard-shortcuts.html + \nextpage studio-projects.html + \else + \previouspage creator-how-tos.html + + \endif + + \title Import and export keyboard shortcuts + + You can use different keyboard shortcut mapping schemes that are stored as + .kms files. + + To import and export keyboard shortcut mapping schemes: + + \list 1 + + \li Select \preferences > \uicontrol Environment > \uicontrol Keyboard. + \image qtcreator-keyboard-shortcuts.png {Keyboard preferences} + + \li To import a keyboard shortcut mapping scheme, click \uicontrol Import + and select the .kms file that has the keyboard shortcut mapping scheme + you want to import. + + \li To export the current keyboard shortcut mapping scheme, click + \uicontrol Export and select the location where you want to save the + exported .kms file. + + \endlist + + \sa {Keyboard Shortcuts}, {Assign keyboard shortcuts}, + {Find keyboard shortcuts} +*/ diff --git a/doc/qtcreator/src/howto/creator-sessions.qdoc b/doc/qtcreator/src/howto/creator-sessions.qdoc index 348bea2139a..646e4fbc330 100644 --- a/doc/qtcreator/src/howto/creator-sessions.qdoc +++ b/doc/qtcreator/src/howto/creator-sessions.qdoc @@ -11,9 +11,10 @@ \page creator-project-managing-sessions.html \if defined(qtdesignstudio) \previouspage creator-project-managing-workspaces.html - \nextpage creator-keyboard-shortcuts.html + \nextpage qtdesignstudio-keyboard-shortcuts.html \else \previouspage creator-how-tos.html + \nextpage creator-keyboard-shortcuts.html \endif \ingroup creator-how-to-use diff --git a/doc/qtcreator/src/howto/creator-keyboard-shortcuts.qdoc b/doc/qtcreator/src/overview/creator-only/creator-keyboard-shortcuts.qdoc similarity index 76% rename from doc/qtcreator/src/howto/creator-keyboard-shortcuts.qdoc rename to doc/qtcreator/src/overview/creator-only/creator-keyboard-shortcuts.qdoc index bbc5942cae4..cf324f5ff9f 100644 --- a/doc/qtcreator/src/howto/creator-keyboard-shortcuts.qdoc +++ b/doc/qtcreator/src/overview/creator-only/creator-keyboard-shortcuts.qdoc @@ -9,12 +9,7 @@ /*! \page creator-keyboard-shortcuts.html - \if defined(qtdesignstudio) - \previouspage creator-project-managing-sessions.html - \nextpage studio-projects.html - \else \previouspage creator-reference.html - \endif \ingroup creator-reference @@ -33,15 +28,14 @@ context. If several functions are available for the same shortcut at a time, there is a conflict and \QC cannot execute any function. - A keyboard shortcut might also conflict with a shortcut that a Window + A keyboard shortcut might also conflict with a shortcut that a window manager uses for its own purposes. In that case, \QC shortcuts do not work. Typically, you can configure the shortcuts in the window manager, but if that is not allowed, you can change the \QC shortcuts. - \if defined(qtcreator) + For example, Unity on Ubuntu 11.10 uses \key F10 in its window manager, and therefore the default \QC keyboard shortcut \key F10 (Step Over) does not work on that system. - \endif To override the platform default value that determines whether @@ -128,11 +122,9 @@ \row \li Previous open document in history \li Ctrl+Tab - \if defined(qtcreator) \row \li Activate \uicontrol Locator \li Ctrl+K - \endif \row \li Switch to \uicontrol Welcome mode \li Ctrl+1 @@ -168,11 +160,9 @@ \li Alt+number (Cmd+number on \macos) Where the number is the number of the view. - \if defined(qtcreator) \row \li Activate \uicontrol Bookmarks view \li Alt+M - \endif \row \li Activate \uicontrol{File System} view \li Alt+Y @@ -200,7 +190,6 @@ \row \li Undo \li Ctrl+Z - \if defined(qtcreator) \row \li Move to \uicontrol Edit mode @@ -210,7 +199,6 @@ \li The second press closes secondary windows \endlist \li Esc - \endif \row \li Exit \QC @@ -343,7 +331,6 @@ \row \li Visualize whitespace \li Ctrl+E, Ctrl+V - \if defined(qtcreator) \row \li Toggle bookmark \li Ctrl+M @@ -359,24 +346,19 @@ \row \li Paste snippet \li Alt+C, Alt+P - \endif \row \li Find references to symbol under cursor \li Ctrl+Shift+U - \if defined(qtcreator) \note If this keyboard shortcut does not work on Linux, see \l {Editing Issues}. - \endif \row \li Follow symbol under cursor Works with namespaces, classes, functions, variables, include statements, and macros, as well as CMake functions, macros, targets, and packages. Also, opens URLs in the default browser - \if defined(qtcreator) and Qt resource files (.qrc) in the \l{Resource Files} {resource editor} - \endif \li F2 \row \li Rename symbol under cursor @@ -387,11 +369,9 @@ \row \li Open type hierarchy \li Ctrl+Shift+T - \if defined(qtcreator) \row \li Switch between header and source file \li F4 - \endif \row \li Add a cursor at the next occurrence of selected text for multi-cursor editing @@ -429,7 +409,6 @@ \row \li Open advanced find \li Ctrl+Shift+F - \if defined(qtcreator) \row \li Record a text-editing macro \li Alt+[ @@ -445,10 +424,8 @@ \row \li Execute user actions in FakeVim mode \li Alt+Y, n, where n is the number of the user action, from 1 to 9 - \endif \endtable - \if defined(qtcreator) \section1 Emacs Shortcuts You can specify shortcuts for executing actions in a way that is familiar to @@ -535,35 +512,7 @@ \li Original size \li Ctrl+0 \endtable - \endif - \if defined(qtdesignstudio) - \section1 Design Mode Keyboard Shortcuts - - You can use the following keyboard shortcuts when editing QML files in the - \uicontrol Design mode. - - \table - \header - \li Action - \li Keyboard shortcut - \row - \li Open the QML file that defines the selected component - \li F2 - \row - \li Move between \uicontrol the {Code} and - \uicontrol {2D} views - \li F4 - \row - \li Toggle left sidebar - \li Ctrl+Alt+0 - \row - \li Toggle right sidebar - \li Ctrl+Alt+Shift+0 - \endtable - \endif - - \if defined(qtcreator) \section1 Debugging Keyboard Shortcuts \table @@ -601,7 +550,6 @@ \li Reverse direction \li F12 \endtable - \endif \section1 Project Keyboard Shortcuts @@ -609,25 +557,21 @@ \header \li Action \li Keyboard shortcut - \if defined(qtcreator) \row \li Build project \li Ctrl+B \row \li Build all \li Ctrl+Shift+B - \endif \row \li New project \li Ctrl+Shift+N \row \li Open project \li Ctrl+Shift+O - \if defined(qtcreator) \row \li Select the \l{glossary-buildandrun-kit}{kit} to build and run your project with \li Ctrl+T - \endif \row \li Run \li Ctrl+R @@ -661,7 +605,6 @@ \section1 Version Control Keyboard Shortcuts - \if defined(qtcreator) \table \header \li {1,2} Action @@ -762,171 +705,7 @@ \li Alt+P, Alt+O \li \endtable - \else - \table - \header - \li Action - \li Keyboard shortcut - \row - \li Stage file for commit - \li Alt+G, Alt+A - \row - \li Commit - \li Alt+G, Alt+C - \row - \li Diff current file - \li Alt+G, Alt+D - \row - \li Diff project - \li Alt+G, Alt+Shift+D - \row - \li Blame - \li Alt+G, Alt+B - \row - \li Log current file - \li Alt+G, Alt+L - \row - \li Log project - \li Alt+G, Alt+K - \row - \li Reset - \li Alt+G, Alt+U - \endtable - \endif \sa {Assign keyboard shortcuts}, {Find keyboard shortcuts}, - {Import and export keyboard shortcuts} - - \if defined(qtcreator) - \sa {Enable and disable plugins} - \endif -*/ - -/*! - \page creator-how-to-assign-keyboard-shortcuts.html - \if defined(qtdesignstudio) - \previouspage creator-keyboard-shortcuts.html - \nextpage studio-projects.html - \else - \previouspage creator-how-tos.html - - \endif - - \ingroup creator-how-to-ui - - \title Assign keyboard shortcuts - - If your favorite keyboard shortcut is missing, you can add it. Also, you can - assign your own keyboard shortcuts for functions that you can easily perform - with a mouse, and that therefore do not appear in menus or have default - keyboard shortcuts. For example, selecting and deleting words or lines in an - editor. - - To change an existing keyboard shortcut or assign a new one: - - \list 1 - - \li Select \preferences > \uicontrol Environment > \uicontrol Keyboard. - \image qtcreator-keyboard-shortcuts.png {Keyboard preferences} - - \li Select a command from the list. - - \li In the \uicontrol{Key Sequence} field, you have the following - options: - - \list - - \li Enter the shortcut key you want to associate with the - selected command. - - \li Record a key sequence. - - \endlist - - \li To assign multiple keyboard shortcuts to a function, select - \uicontrol Add, and enter or record an additional key combination. - - \li To revert to the default shortcut, select \uicontrol Reset. - - \endlist - - \section1 Record key sequences - - \list 1 - \li Select \uicontrol Record. - \li Press the keys to use as the keyboard shortcut. - \li Select \uicontrol {Stop Recording} when you are done. - \endlist - - \sa {Keyboard Shortcuts}, {Find keyboard shortcuts}, - {Import and export keyboard shortcuts} -*/ - -/*! - \page creator-how-to-find-keyboard-shortcuts.html - \if defined(qtdesignstudio) - \previouspage creator-keyboard-shortcuts.html - \nextpage studio-projects.html - \else - \previouspage creator-how-tos.html - - \endif - - \ingroup creator-how-to-ui - - \title Find keyboard shortcuts - - \QC has many useful keyboard shortcuts. You can see the keyboard shortcut for - a menu command in the menu or the tooltip for a button or in the keyboard - preferences. - - To look up keyboard shortcuts: - - \list 1 - \li Select \preferences > \uicontrol Environment > \uicontrol Keyboard. - \image qtcreator-keyboard-shortcuts.png {Keyboard preferences} - \li Start typing the name of a function or shortcut in the - \uicontrol Filter field. - \endlist - - You can change the existing keyboard shortcuts or import and export them. - - \sa {Keyboard Shortcuts}, {Assign keyboard shortcuts}, - {Import and export keyboard shortcuts} -*/ - -/*! - \page creator-how-to-change-keyboard-shortcuts.html - \if defined(qtdesignstudio) - \previouspage creator-keyboard-shortcuts.html - \nextpage studio-projects.html - \else - \previouspage creator-how-tos.html - - \endif - - \title Import and export keyboard shortcuts - - You can use different keyboard shortcut mapping schemes that are stored as - .kms files. - - To import and export keyboard shortcut mapping schemes: - - \list 1 - - \li Select \preferences > \uicontrol Environment > \uicontrol Keyboard. - \image qtcreator-keyboard-shortcuts.png {Keyboard preferences} - - \li To import a keyboard shortcut mapping scheme, click \uicontrol Import - and select the .kms file that has the keyboard shortcut mapping scheme - you want to import. - - \li To export the current keyboard shortcut mapping scheme, click - \uicontrol Export and select the location where you want to save the - exported .kms file. - - \endlist - - \sa {Keyboard Shortcuts}, {Assign keyboard shortcuts}, - {Find keyboard shortcuts} + {Import and export keyboard shortcuts}, {Enable and disable plugins} */ diff --git a/doc/qtdesignstudio/src/qtdesignstudio-projects.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-projects.qdoc index e43232423f1..639b4d45fd9 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-projects.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-projects.qdoc @@ -3,7 +3,7 @@ /*! \page studio-projects.html - \previouspage creator-keyboard-shortcuts.html + \previouspage qtdesignstudio-keyboard-shortcuts.html \nextpage studio-use-cases.html \title Creating Projects diff --git a/doc/qtdesignstudio/src/reference/qtdesignstudio-keyboard-shortcuts.qdoc b/doc/qtdesignstudio/src/reference/qtdesignstudio-keyboard-shortcuts.qdoc new file mode 100644 index 00000000000..ac6354ca90b --- /dev/null +++ b/doc/qtdesignstudio/src/reference/qtdesignstudio-keyboard-shortcuts.qdoc @@ -0,0 +1,465 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +/*! + \page studio-keyboard-shortcuts.html + \previouspage creator-project-managing-sessions.html + \nextpage studio-projects.html + + \title Keyboard Shortcuts + + \brief Default keyboard shortcuts. + + \QDS has various keyboard shortcuts that speed up your development process. + To view all \QDS functions in and their keyboard shortcuts, select + \preferences > \uicontrol Environment > \uicontrol Keyboard. + + \image qtcreator-keyboard-shortcuts.png + + The shortcuts that are displayed in red color are associated with several + functions. \QDS executes the function that is available in the current + context. If several functions are available for the same shortcut at a + time, there is a conflict and \QDS cannot execute any function. + + A keyboard shortcut might also conflict with a shortcut that a window + manager uses for its own purposes. In that case, \QDS shortcuts do not work. + Typically, you can configure the shortcuts in the window manager, but if + that is not allowed, you can change the \QDS shortcuts. + + + To override the platform default value that determines whether + keyboard shortcuts are shown in the labels of context menu items, + select \preferences > \uicontrol Environment > + \uicontrol Interface. The label of the \uicontrol {Show keyboard shortcuts + in context menus} check box indicates whether the platform default value + is \c on or \c off. + + \image qtcreator-preferences-environment-interface.webp {Interface tab in Environment preferences} + + The following tables list the default keyboard shortcuts. They are + categorized by actions. + + \section1 General Keyboard Shortcuts + + \table + \header + \li Action + \li Keyboard shortcut + \row + \li Open file or project + \li \key{Ctrl+O} + \row + \li New project + \li \key{Ctrl+Shift+N} + \row + \li New file + \li \key{Ctrl+N} + \row + \li Select all + \li \key{Ctrl+A} + \row + \li Delete + \li \key{Del} + \row + \li Cut + \li \key{Ctrl+X} + \row + \li Copy + \li \key{Ctrl+C} + \row + \li Paste + \li \key{Ctrl+V} + \row + \li Undo + \li \key{Ctrl+Z} + \row + \li Redo + \li \key{Ctrl+Y} + \row + \li Print + \li \key{Ctrl+P} + \row + \li Save + \li \key{Ctrl+S} + \row + \li Save all + \li \key{Ctrl+Shift+S} + \row + \li Close window + \li \key{Ctrl+W} + \row + \li Close all + \li \key{Ctrl+Shift+W} + \row + \li Close current file + \li \key{Ctrl+F4} + \row + \li Go back + \li \key{Alt+Left} + \row + \li Go forward + \li \key{Alt+Right} + \row + \li Go to line + \li \key{Ctrl+L} + \row + \li Locate files and other content within the project + \li \key{Ctrl+K} + \row + \li Next open document in history + \li \key{Ctrl+Shift+Tab} + \row + \li Previous open document in history + \li \key{Ctrl+Tab} + \row + \li Switch to \uicontrol Welcome mode + \li \key{Ctrl+1} + \row + \li Switch to \uicontrol Edit mode + \li \key{Ctrl+2} + \row + \li Switch to \uicontrol Design mode + \li \key{Ctrl+3} + \row + \li Switch to \uicontrol Debug mode + \li \key{Ctrl+4} + \row + \li Switch to \uicontrol Projects mode + \li \key{Ctrl+5} + \row + \li Switch to \uicontrol Help mode + \li \key{Ctrl+6} + \row + \li Toggle \uicontrol{Issues} + \li \key{Alt+1} (\key{Cmd+1} on \macos) + \row + \li Toggle \uicontrol{Search Results} + \li \key{Alt+2} (\key{Cmd+2} on \macos) + \row + \li Toggle \uicontrol{Application Output} + \li \key{Alt+3} (\key{Cmd+3} on \macos) + \row + \li Toggle \uicontrol{Compile Output} + \li \key{Alt+4} (\key{Cmd+4} on \macos) + \row + \li Toggle \uicontrol{Terminal} + \li \key{Alt+5} (\key{Cmd+5} on \macos) + \row + \li Toggle \uicontrol{Version Control} + \li \key{Alt+6} (\key{Cmd+6} on \macos) + \row + \li Toggle \uicontrol{QML Debugger Console} + \li \key{Alt+7} (\key{Cmd+7} on \macos) + \row + \li Toggle \uicontrol{General Messages} + \li \key{Alt+8} (\key{Cmd+8} on \macos) + \row + \li Maximize output views + \li \key{Alt+Shift+9} + \row + \li Move to next item in output + \li \key{F6} + \row + \li Move to previous item in output + \li \key{Shift+F6} + \row + \li Activate \uicontrol Projects view + \li \key{Alt+X} + \row + \li Activate \uicontrol{Open Documents} view + \li \key{Alt+O} + \row + \li Activate \uicontrol{File System} view + \li \key{Alt+Y}, \key{Alt+F} + \row + \li Toggle left sidebar + \li \key{Alt+0} (\key{Cmd+0} on \macos) + \row + \li Toggle right sidebar + \li \key{Alt+Shift+0} + \row + \li Full screen + \li \key{Ctrl+Shift+F11} + \row + \li Exit \QDS + + By default, \QDS exits without asking for confirmation, unless + there are unsaved changes in open files. To always be asked, + select the \uicontrol {Ask for confirmation before exiting} + check box in \preferences > \uicontrol Environment > + \uicontrol System. + \li \key{Ctrl+Q} + \endtable + + \section1 Design Mode Keyboard Shortcuts + + You can use the following keyboard shortcuts when editing QML files in the + \uicontrol Design mode. + + \table + \header + \li Action + \li Keyboard shortcut + \row + \li Open the QML file that defines the selected component + \li \key{F2} + \row + \li Jump to the \uicontrol {Code} view. + \li \key{F4} + \endtable + + \section1 Editing Keyboard Shortcuts + + \table + \header + \li Action + \li Keyboard shortcut + \row + \li Auto-indent selection + \li \key{Ctrl+I} + \row + \li Collapse + \li \key{Ctrl+<} + \row + \li Expand + \li \key{Ctrl+>} + \row + \li Trigger a completion in this scope + \li \key{Ctrl+Space} + \row + \li Display tooltips for function signatures regardless of the + cursor position in the function call + \li \key{Ctrl+Shift+D} + \row + \li Copy line + \li \key{Ctrl+Ins} + \row + \li Copy line down + \li \key{Ctrl+Alt+Down} + \row + \li Copy line up + \li \key{Ctrl+Alt+Up} + \row + \li Paste from the clipboard history + \li \key{Ctrl+Shift+V} + \row + \li Cut line + \li \key{Shift+Del} + \row + \li Join lines + \li \key{Ctrl+J} + \row + \li Insert line above current line + \li \key{Ctrl+Shift+Enter} + \row + \li Insert line below current line + \li \key{Ctrl+Enter} + \row + \li Decrease font size + \li \key{Ctrl+-} (\key{Ctrl+Roll mouse wheel down}) + \row + \li Increase font size + \li \key{Ctrl++} (\key{Ctrl+Roll mouse wheel up}) + \row + \li Reset font size + \li \key{Ctrl+0} + \row + \li Split + \li \key{Ctrl+E}, \key{2} + \row + \li Split side by side + \li \key{Ctrl+E}, \key{3} + \row + \li Remove all splits + \li \key{Ctrl+E}, \key{1} + \row + \li Remove current split + \li \key{Ctrl+E}, \key{0} + \row + \li Go to next split + \li \key{Ctrl+E, O} + \row + \li Select all + \li \key{Ctrl+A} + \row + \li Go to block end + \li \key{Ctrl+]} + \row + \li Go to block start + \li \key{Ctrl+[} + \row + \li Go to block end and select the lines between the current cursor + position and the end of the block + \li \key{Ctrl+Shift+]} + \row + \li Go to block start and select the lines between the current + cursor position and the beginning of the block + \li \key{Ctrl+Shift+[} + \row + \li Select the current block + + The second press extends the selection to the parent block. To + enable this behavior, select \preferences > + \uicontrol {Text Editor} > \uicontrol Behavior > + \uicontrol {Enable smart selection changing}. + \li \key{Ctrl+U} + \row + \li Undo the latest smart block selection + \li \key{Ctrl+Alt+Shift+U} + \row + \li Move current line down + \li \key{Ctrl+Shift+Down} + \row + \li Move current line up + \li \key{Ctrl+Shift+Up} + \row + \li Trigger a refactoring action in this scope + \li \key{Alt+Enter} + \row + \li Rewrap paragraph + \li \key{Ctrl+E}, \key{R} + \row + \li Enable text wrapping + \li \key{Ctrl+E}, \key{Ctrl+W} + \row + \li Toggle comment for selection + \li \key{Ctrl+/} + \row + \li Visualize whitespace + \li \key{Ctrl+E}, \key{Ctrl+V} + \row + \li Find references to symbol under cursor + \li \key{Ctrl+Shift+U} + \row + \li Follow symbol under cursor + + Works with namespaces, classes, functions, variables, include + statements, and macros, as well as CMake functions, macros, + targets, and packages. Also, opens URLs in the default browser. + \li \key{F2} + \row + \li Rename symbol under cursor + \li \key{Ctrl+Shift+R} + \row + \li Switch between function declaration and definition + \li \key{Shift+F2} + \row + \li Open type hierarchy + \li \key{Ctrl+Shift+T} + \row + \li Add a cursor at the next occurrence of selected text for + multi-cursor editing + \li \key{Ctrl+D} + \row + \li Turn selected text into lowercase + \li \key{Alt+U} + \row + \li Turn selected text into uppercase + \li \key{Alt+Shift+U} + \row + \li Sort selected lines alphabetically + \li \key{Alt+Shift+S} + \row + \li Run static checks on JavaScript code to find common problems + \li \key{Ctrl+Shift+C} + \row + \li Find and replace + \li \key{Ctrl+F} + \row + \li Find next + \li \key{F3} + \row + \li Find previous + \li \key{Shift+F3} + \row + \li Find next occurrence of selected text + \li \key{Ctrl+F3} + \row + \li Find previous occurrence of selected text + \li \key{Ctrl+Shift+F3} + \row + \li Replace next + \li \key{Ctrl+=} + \row + \li Open advanced find + \li \key{Ctrl+Shift+F} + \endtable + + \section1 Project Keyboard Shortcuts + + \table + \header + \li Action + \li Keyboard shortcut + \row + \li New project + \li \key{Ctrl+Shift+N} + \row + \li Load project + \li \key{Ctrl+Shift+O} + \row + \li Run + \li \key{Ctrl+R} + \endtable + + \section1 Help Keyboard Shortcuts + + \table + \header + \li Action + \li Keyboard shortcut + \row + \li View context-sensitive help + \li \key{F1} + \row + \li Add bookmark in \uicontrol Help mode + \li \key{Ctrl+M} + \row + \li Activate index in \uicontrol Help mode + \li \key{Ctrl+Shift+I} + \row + \li Activate search in \uicontrol Help mode + \li \key{Ctrl+Shift+/} + \row + \li Activate bookmarks in \uicontrol Help mode + \li \key{ Ctrl+Shift+B} + \row + \li Activate contents in \uicontrol Help mode + \li \key{Ctrl+Shift+C} + \endtable + + \section1 Version Control Keyboard Shortcuts + + \table + \header + \li Action + \li Keyboard shortcut + \row + \li Stage file for commit + \li \key{Alt+G}, \key{Alt+A} + \row + \li Commit + \li \key{Alt+G}, \key{Alt+C} + \row + \li Diff current file + \li \key{Alt+G}, \key{Alt+D} + \row + \li Diff project + \li \key{Alt+G}, \key{Alt+Shift+D} + \row + \li Blame + \li \key{Alt+G}, \key{Alt+B} + \row + \li Log current file + \li \key{Alt+G}, \key{Alt+L} + \row + \li Log project + \li \key{Alt+G}, \key{Alt+K} + \row + \li Reset + \li \key{Alt+G}, \key{Alt+U} + \endtable + + \sa {Assign keyboard shortcuts}, {Find keyboard shortcuts}, + {Import and export keyboard shortcuts} +*/ From 1f9e41ac9ab56a5c5abb5d0c241b4131f11d570a Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Mon, 12 Feb 2024 12:26:17 +0200 Subject: [PATCH 035/176] QmlDesigner: Keep the order of columns for imported jsons Fixes: QDS-11670 Change-Id: Ia068bbb864065b648cffcab2c3477d3cec7f25f8 Reviewed-by: Mahmoud Badri Reviewed-by: Miikka Heikkinen Reviewed-by: Qt CI Patch Build Bot --- .../collectioneditor/collectiondetails.cpp | 75 ++++++++++++++++++- .../collectioneditor/collectiondetails.h | 7 +- .../collectioneditor/collectionwidget.cpp | 4 +- 3 files changed, 77 insertions(+), 9 deletions(-) diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp index 4acd2f601a6..e8e8e18dc0c 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp @@ -6,6 +6,9 @@ #include "collectioneditorutils.h" #include +#include +#include +#include #include #include @@ -261,6 +264,47 @@ inline static bool isEmptyJsonValue(const QJsonValue &value) return value.isNull() || value.isUndefined() || (value.isString() && value.toString().isEmpty()); } +class PropertyOrderFinder : public QmlJS::AST::Visitor +{ +public: + static QStringList parse(const QString &jsonContent) + { + PropertyOrderFinder finder; + QmlJS::Document::MutablePtr jsonDoc = QmlJS::Document::create(Utils::FilePath::fromString( + ""), + QmlJS::Dialect::Json); + + jsonDoc->setSource(jsonContent); + jsonDoc->parseJavaScript(); + + if (!jsonDoc->isParsedCorrectly()) + return {}; + + jsonDoc->ast()->accept(&finder); + return finder.m_orderedList; + } + +protected: + bool visit(QmlJS::AST::PatternProperty *patternProperty) override + { + const QString propertyName = patternProperty->name->asString(); + if (!m_propertySet.contains(propertyName)) { + m_propertySet.insert(propertyName); + m_orderedList.append(propertyName); + } + return true; + } + + void throwRecursionDepthError() override + { + qWarning() << Q_FUNC_INFO << __LINE__ << "Recursion depth error"; + }; + +private: + QSet m_propertySet; + QStringList m_orderedList; +}; + QString CollectionParseError::errorString() const { switch (errorNo) { @@ -690,7 +734,7 @@ CollectionDetails CollectionDetails::fromImportedCsv(const QByteArray &document) return fromImportedJson(importedArray); } -CollectionDetails CollectionDetails::fromImportedJson(const QJsonDocument &document) +CollectionDetails CollectionDetails::fromImportedJson(const QByteArray &json, QJsonParseError *error) { QJsonArray importedCollection; auto refineJsonArray = [](const QJsonArray &array) -> QJsonArray { @@ -710,6 +754,14 @@ CollectionDetails CollectionDetails::fromImportedJson(const QJsonDocument &docum return resultArray; }; + QJsonParseError parseError; + QJsonDocument document = QJsonDocument::fromJson(json, &parseError); + if (error) + *error = parseError; + + if (parseError.error != QJsonParseError::NoError) + return CollectionDetails{}; + if (document.isArray()) { importedCollection = refineJsonArray(document.array()); } else if (document.isObject()) { @@ -738,7 +790,7 @@ CollectionDetails CollectionDetails::fromImportedJson(const QJsonDocument &docum } } - return fromImportedJson(importedCollection); + return fromImportedJson(importedCollection, PropertyOrderFinder::parse(QLatin1String(json))); } CollectionDetails CollectionDetails::fromLocalJson(const QJsonDocument &document, @@ -803,9 +855,24 @@ void CollectionDetails::insertRecords(const QJsonArray &record, int idx, int cou d->dataRecords.insert(idx, count, localRecord); } -CollectionDetails CollectionDetails::fromImportedJson(const QJsonArray &importedArray) +CollectionDetails CollectionDetails::fromImportedJson(const QJsonArray &importedArray, + const QStringList &propertyPriority) { - const QList columnData = getColumnsFromImportedJsonArray(importedArray); + QList columnData = getColumnsFromImportedJsonArray(importedArray); + if (!propertyPriority.isEmpty()) { + QMap priorityMap; + for (const QString &propertyName : propertyPriority) { + if (!priorityMap.contains(propertyName)) + priorityMap.insert(propertyName, priorityMap.size()); + } + const int lowestPriority = priorityMap.size(); + + Utils::sort(columnData, [&](const CollectionProperty &a, const CollectionProperty &b) { + return priorityMap.value(a.name, lowestPriority) + < priorityMap.value(b.name, lowestPriority); + }); + } + QList localJsonArray; for (const QJsonValue &importedRowValue : importedArray) { QJsonObject importedRowObject = importedRowValue.toObject(); diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h index 2ccc69e4477..95528413f6c 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h @@ -9,6 +9,7 @@ QT_BEGIN_NAMESPACE class QJsonObject; +struct QJsonParseError; class QVariant; QT_END_NAMESPACE @@ -125,7 +126,8 @@ public: static void registerDeclarativeType(); static CollectionDetails fromImportedCsv(const QByteArray &document); - static CollectionDetails fromImportedJson(const QJsonDocument &document); + static CollectionDetails fromImportedJson(const QByteArray &json, + QJsonParseError *error = nullptr); static CollectionDetails fromLocalJson(const QJsonDocument &document, const QString &collectionName, CollectionParseError *error = nullptr); @@ -136,7 +138,8 @@ private: void markChanged(); void insertRecords(const QJsonArray &record, int idx = -1, int count = 1); - static CollectionDetails fromImportedJson(const QJsonArray &importedArray); + static CollectionDetails fromImportedJson(const QJsonArray &importedArray, + const QStringList &propertyPriority = {}); static CollectionDetails fromLocalCollection(const QJsonObject &localCollection, CollectionParseError *error = nullptr); diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp index efffa2c2e93..24fb745ab77 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp @@ -306,13 +306,11 @@ bool CollectionWidget::importFile(const QString &collectionName, const QUrl &url return false; QJsonParseError parseError; - QJsonDocument document = QJsonDocument::fromJson(fileContent, &parseError); + loadedCollection = CollectionDetails::fromImportedJson(fileContent, &parseError); if (parseError.error != QJsonParseError::NoError) { warn(tr("Json file Import error"), tr("Cannot parse json content\n%1").arg(parseError.errorString())); } - - loadedCollection = CollectionDetails::fromImportedJson(document); } else if (fileInfo.suffix() == "csv") { if (!loadUrlContent()) return false; From c90970afed8254aeda20b6496ca09366dd701237 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Mon, 12 Feb 2024 16:04:57 +0200 Subject: [PATCH 036/176] QmlDesigner: Insert proper value for booleans to CSV exported files Fixes: QDS-11823 Change-Id: I4d5a73b982261f59192d7877fe682057c5181d7b Reviewed-by: Miikka Heikkinen --- .../components/collectioneditor/collectiondetails.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp index e8e8e18dc0c..683d4112eb3 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp @@ -659,6 +659,8 @@ QString CollectionDetails::toCsv() const if (value.isDouble()) content += QString::number(value.toDouble()) + ','; + else if (value.isBool()) + content += value.toBool() ? "true," : "false,"; else content += value.toString() + ','; } From 93993c322e7f8b832e63cb131813d567a5352fdd Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 9 Feb 2024 16:31:30 +0200 Subject: [PATCH 037/176] EffectComposer: Fix effect preview zoom Zoom must scale the component showing the preview, not the source image. This way the effect stays consistent regardless of the zoom level. Fixes: QDS-11899 Change-Id: I550eb9ff693c24a853f5c25d9d79fa146448663f Reviewed-by: Mahmoud Badri --- .../EffectComposerPreview.qml | 237 ++++++++++-------- .../effectcomposer/effectcomposermodel.h | 2 +- 2 files changed, 129 insertions(+), 110 deletions(-) diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerPreview.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerPreview.qml index 50c8e32c715..c832b2f3708 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerPreview.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerPreview.qml @@ -22,11 +22,13 @@ Column { readonly property int previewMargin: 5 + property real previewScale: 1 + // Create a dummy parent to host the effect qml object function createNewComponent() { // If we have a working effect, do not show preview image as it shows through // transparent parts of the final image - source.visible = false; + placeHolder.visible = false; var oldComponent = componentParent.children[0]; if (oldComponent) @@ -48,7 +50,7 @@ Column { errorLine = e.lineNumber; } effectComposerModel.setEffectError(errorString, 0, errorLine); - source.visible = true; + placeHolder.visible = true; } } @@ -84,42 +86,42 @@ Column { anchors.verticalCenter: parent.verticalCenter HelperWidgets.AbstractButton { - enabled: sourceImage.scale < 3 + enabled: root.previewScale < 3 style: StudioTheme.Values.viewBarButtonStyle buttonIcon: StudioTheme.Constants.zoomIn_medium tooltip: qsTr("Zoom In") onClicked: { - sourceImage.enableAnim(true) - sourceImage.scale += .2 - sourceImage.enableAnim(false) + imageScaler.enableAnim(true) + root.previewScale += .2 + imageScaler.enableAnim(false) zoomIndicator.show() } } HelperWidgets.AbstractButton { - enabled: sourceImage.scale > .4 + enabled: root.previewScale > .4 style: StudioTheme.Values.viewBarButtonStyle buttonIcon: StudioTheme.Constants.zoomOut_medium tooltip: qsTr("Zoom out") onClicked: { - sourceImage.enableAnim(true) - sourceImage.scale -= .2 - sourceImage.enableAnim(false) + imageScaler.enableAnim(true) + root.previewScale -= .2 + imageScaler.enableAnim(false) zoomIndicator.show() } } HelperWidgets.AbstractButton { - enabled: sourceImage.scale !== 1 || sourceImage.x !== root.previewMargin - || sourceImage.y !== root.previewMargin + enabled: root.previewScale !== 1 || imageScaler.x !== root.previewMargin + || imageScaler.y !== root.previewMargin style: StudioTheme.Values.viewBarButtonStyle buttonIcon: StudioTheme.Constants.fitAll_medium tooltip: qsTr("Reset View") onClicked: { - sourceImage.resetTransforms() + imageScaler.resetTransforms() } } } @@ -187,8 +189,8 @@ Column { property bool panning: false onPressed: { - pressX = mouseX - sourceImage.x - pressY = mouseY - sourceImage.y + pressX = mouseX - imageScaler.x + pressY = mouseY - imageScaler.y panning = true } @@ -197,22 +199,21 @@ Column { } onWheel: (wheel) => { - let prevScale = sourceImage.scale + let oldPoint = imageScaler.mapFromItem(mouseArea, Qt.point(wheel.x, wheel.y)) if (wheel.angleDelta.y > 0) { - if (sourceImage.scale < 3) - sourceImage.scale += .2 + if (root.previewScale < 3) + root.previewScale += .2 } else { - if (sourceImage.scale > .4) - sourceImage.scale -= .2 + if (root.previewScale > .4) + root.previewScale -= .2 } - let dScale = sourceImage.scale - prevScale + let newPoint = imageScaler.mapFromItem(mouseArea, Qt.point(wheel.x, wheel.y)) + imageScaler.x -= (oldPoint.x - newPoint.x) * imageScaler.scale + imageScaler.y -= (oldPoint.y - newPoint.y) * imageScaler.scale - sourceImage.x += (sourceImage.x + sourceImage.width * .5 - wheel.x) * dScale; - sourceImage.y += (sourceImage.y + sourceImage.height * .5 - wheel.y) * dScale; - - sourceImage.checkBounds() + imageScaler.checkBounds() zoomIndicator.show() } @@ -222,98 +223,40 @@ Column { repeat: true onTriggered: { - sourceImage.x = mouseArea.mouseX - mouseArea.pressX - sourceImage.y = mouseArea.mouseY - mouseArea.pressY - sourceImage.checkBounds() + imageScaler.x = mouseArea.mouseX - mouseArea.pressX + imageScaler.y = mouseArea.mouseY - mouseArea.pressY + imageScaler.checkBounds() } } } + Image { + id: placeHolder + anchors.fill: parent + anchors.margins: root.previewMargin + fillMode: Image.PreserveAspectFit + source: imagesComboBox.selectedImage + smooth: true + } + Item { // Source item as a canvas (render target) for effect id: source - anchors.fill: parent + width: sourceImage.sourceSize.width + height: sourceImage.sourceSize.height layer.enabled: true layer.mipmap: true layer.smooth: true + visible: false Image { id: sourceImage - function checkBounds() { - let edgeMargin = 10 - // correction factor to account for an observation that edgeMargin decreases - // with increased zoom - let corrFactor = 10 * sourceImage.scale - let imgW2 = sourceImage.paintedWidth * sourceImage.scale * .5 - let imgH2 = sourceImage.paintedHeight * sourceImage.scale * .5 - let srcW2 = source.width * .5 - let srcH2 = source.height * .5 + onSourceChanged: imageScaler.resetTransforms() - if (sourceImage.x < -srcW2 - imgW2 + edgeMargin + corrFactor) - sourceImage.x = -srcW2 - imgW2 + edgeMargin + corrFactor - else if (x > srcW2 + imgW2 - edgeMargin - corrFactor) - sourceImage.x = srcW2 + imgW2 - edgeMargin - corrFactor + fillMode: Image.Pad - if (sourceImage.y < -srcH2 - imgH2 + edgeMargin + corrFactor) - sourceImage.y = -srcH2 - imgH2 + edgeMargin + corrFactor - else if (y > srcH2 + imgH2 - edgeMargin - corrFactor) - sourceImage.y = srcH2 + imgH2 - edgeMargin - corrFactor - } - - function resetTransforms() { - sourceImage.enableAnim(true) - sourceImage.scale = 1 - sourceImage.x = root.previewMargin - sourceImage.y = root.previewMargin - sourceImage.enableAnim(false) - } - - function enableAnim(flag) { - xBehavior.enabled = flag - yBehavior.enabled = flag - scaleBehavior.enabled = flag - } - - onSourceChanged: sourceImage.resetTransforms() - - fillMode: Image.PreserveAspectFit - - x: root.previewMargin - y: root.previewMargin - width: parent.width - root.previewMargin * 2 - height: parent.height - root.previewMargin * 2 source: imagesComboBox.selectedImage smooth: true - - Behavior on x { - id: xBehavior - - enabled: false - NumberAnimation { - duration: 200 - easing.type: Easing.OutQuad - } - } - - Behavior on y { - id: yBehavior - - enabled: false - NumberAnimation { - duration: 200 - easing.type: Easing.OutQuad - } - } - - Behavior on scale { - id: scaleBehavior - - enabled: false - NumberAnimation { - duration: 200 - easing.type: Easing.OutQuad - } - } } } @@ -325,14 +268,90 @@ Column { } Item { - id: componentParent - width: source.width - height: source.height - anchors.centerIn: parent - // Cache the layer. This way heavy shaders rendering doesn't - // slow down code editing & rest of the UI. - layer.enabled: true - layer.smooth: true + id: imageScaler + x: root.previewMargin + y: root.previewMargin + width: parent.width - root.previewMargin * 2 + height: parent.height - root.previewMargin * 2 + + scale: root.previewScale * (width > height ? height / sourceImage.sourceSize.height + : width / sourceImage.sourceSize.width) + + Behavior on x { + id: xBehavior + + enabled: false + NumberAnimation { + duration: 200 + easing.type: Easing.OutQuad + } + } + + Behavior on y { + id: yBehavior + + enabled: false + NumberAnimation { + duration: 200 + easing.type: Easing.OutQuad + } + } + + Behavior on scale { + id: scaleBehavior + + enabled: false + NumberAnimation { + duration: 200 + easing.type: Easing.OutQuad + } + } + + function checkBounds() { + let edgeMargin = 10 + // correction factor to account for an observation that edgeMargin decreases + // with increased zoom + let corrFactor = 10 * imageScaler.scale + let imgW2 = sourceImage.paintedWidth * imageScaler.scale * .5 + let imgH2 = sourceImage.paintedHeight * imageScaler.scale * .5 + let srcW2 = width * .5 + let srcH2 = height * .5 + + if (imageScaler.x < -srcW2 - imgW2 + edgeMargin + corrFactor) + imageScaler.x = -srcW2 - imgW2 + edgeMargin + corrFactor + else if (x > srcW2 + imgW2 - edgeMargin - corrFactor) + imageScaler.x = srcW2 + imgW2 - edgeMargin - corrFactor + + if (imageScaler.y < -srcH2 - imgH2 + edgeMargin + corrFactor) + imageScaler.y = -srcH2 - imgH2 + edgeMargin + corrFactor + else if (y > srcH2 + imgH2 - edgeMargin - corrFactor) + imageScaler.y = srcH2 + imgH2 - edgeMargin - corrFactor + } + + function resetTransforms() { + imageScaler.enableAnim(true) + root.previewScale = 1 + imageScaler.x = root.previewMargin + imageScaler.y = root.previewMargin + imageScaler.enableAnim(false) + } + + function enableAnim(flag) { + xBehavior.enabled = flag + yBehavior.enabled = flag + scaleBehavior.enabled = flag + } + + Item { + id: componentParent + width: source.width + height: source.height + anchors.centerIn: parent + // Cache the layer. This way heavy shaders rendering doesn't + // slow down code editing & rest of the UI. + layer.enabled: true + layer.smooth: true + } } Rectangle { @@ -349,7 +368,7 @@ Column { } Text { - text: Math.round(sourceImage.scale * 100) + "%" + text: Math.round(root.previewScale * 100) + "%" color: StudioTheme.Values.themeTextColor anchors.centerIn: parent } diff --git a/src/plugins/effectcomposer/effectcomposermodel.h b/src/plugins/effectcomposer/effectcomposermodel.h index c0eb76f0b94..871d0c72980 100644 --- a/src/plugins/effectcomposer/effectcomposermodel.h +++ b/src/plugins/effectcomposer/effectcomposermodel.h @@ -178,7 +178,7 @@ private: QList m_nodes; int m_selectedIndex = -1; - bool m_isEmpty = true; + bool m_isEmpty = false; // Init to false to force initial bake after setup bool m_hasUnsavedChanges = false; // True when shaders haven't changed since last baking bool m_shadersUpToDate = true; From e66b19d4c4ac591bddfaee26b7ad283690cb9149 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Mon, 12 Feb 2024 17:06:34 +0200 Subject: [PATCH 038/176] EffectComposer: Add confirmation dialog to clear all effect nodes Fixes: QDS-11445 Change-Id: Iedd41e2799148c6d8f4eb3bca77dd5f1b7b053e6 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri --- .../ConfirmClearAllDialog.qml | 47 +++++++++++++++++++ .../EffectComposer.qml | 12 ++++- 2 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 share/qtcreator/qmldesigner/effectComposerQmlSources/ConfirmClearAllDialog.qml diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/ConfirmClearAllDialog.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/ConfirmClearAllDialog.qml new file mode 100644 index 00000000000..146f7edef1b --- /dev/null +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/ConfirmClearAllDialog.qml @@ -0,0 +1,47 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQuick +import QtQuick.Controls +import HelperWidgets as HelperWidgets +import StudioControls as StudioControls +import StudioTheme as StudioTheme +import EffectComposerBackend + +StudioControls.Dialog { + id: root + + title: qsTr("Confirm clear list") + + closePolicy: Popup.CloseOnEscape + modal: true + implicitWidth: 270 + implicitHeight: 150 + + contentItem: Item { + Text { + text: qsTr("You are about to clear the list of effect nodes.\n\nThis can not be undone.") + color: StudioTheme.Values.themeTextColor + } + + Row { + anchors.right: parent.right + anchors.bottom: parent.bottom + spacing: 2 + + HelperWidgets.Button { + text: qsTr("Clear") + onClicked: { + EffectComposerBackend.effectComposerModel.clear() + root.accept() + } + } + + HelperWidgets.Button { + anchors.bottom: parent.bottom + text: qsTr("Cancel") + onClicked: root.reject() + } + } + } +} diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposer.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposer.qml index 95ab2c49bd4..7b4d8d04cf1 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposer.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposer.qml @@ -65,6 +65,11 @@ ColumnLayout { } } + ConfirmClearAllDialog { + id: confirmClearAllDialog + anchors.centerIn: parent + } + EffectComposerTopBar { Layout.fillWidth: true @@ -157,7 +162,12 @@ ColumnLayout { tooltip: qsTr("Remove all effect nodes.") enabled: !root.backendModel.isEmpty - onClicked: root.backendModel.clear() + onClicked: { + if (root.backendModel.hasUnsavedChanges) + confirmClearAllDialog.open() + else + root.backendModel.clear() + } } HelperWidgets.AbstractButton { From 4c4e90062c071005d8dd5aaab09a952054301ffc Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Mon, 12 Feb 2024 18:45:05 +0200 Subject: [PATCH 039/176] QmlDesigner: Update the value of the color picker for CollectionEditor Fixes: QDS-11796 Change-Id: Iaf3f6bc06771f461c4119d55ae3c3a294d3d8564 Reviewed-by: Miikka Heikkinen Reviewed-by: Qt CI Patch Build Bot --- .../ColorViewDelegate.qml | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/ColorViewDelegate.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/ColorViewDelegate.qml index 1414a2dd3a2..6bb1b60159a 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/ColorViewDelegate.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/ColorViewDelegate.qml @@ -42,6 +42,19 @@ Row { property bool __block: false + function getColorFromEditValue() { + if (!edit) + return "white" // default color for Rectangle + + if (colorEditor.isVector3D) { + return Qt.rgba(__editColor.x, + __editColor.y, + __editColor.z, 1) + } + + return __editColor + } + function resetShapeColor() { if (edit) edit = "" @@ -64,7 +77,7 @@ Row { // Syncing color from backend to frontend and block reflection function syncColor() { colorEditor.__block = true - colorEditor.color = colorEditor.value + colorEditor.color = colorEditor.getColorFromEditValue() hexTextField.syncColor() colorEditor.__block = false } @@ -201,10 +214,10 @@ Row { } function isSolid() { - if (!loader.active) - return true + if (!loader.active) + return true - return popupDialog.loaderItem.isSolid() + return popupDialog.loaderItem.isSolid() } function isLinearGradient(){ @@ -221,6 +234,7 @@ Row { function open() { popupDialog.ensureLoader() + popupDialog.loaderItem.initEditor() popupDialog.show(preview) } From ba536a5e9a23bf419edeb1651132a896acaabbef Mon Sep 17 00:00:00 2001 From: Teea Poldsam Date: Mon, 12 Feb 2024 15:39:56 +0200 Subject: [PATCH 040/176] Doc: Fix a broken link MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes: QDS-11930 Change-Id: Ide113597e9a1f3b1958946431db4fceaad41b069 Reviewed-by: Esa Törmänen Reviewed-by: Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mats Honkamaa --- .../src/external-resources/external-resources-qds.qdoc | 4 ++-- .../src/mcus/qtdesignstudio-creating-uis-for-mcus.qdoc | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/qtcreator/src/external-resources/external-resources-qds.qdoc b/doc/qtcreator/src/external-resources/external-resources-qds.qdoc index 610e0926344..ab6afa3fa56 100644 --- a/doc/qtcreator/src/external-resources/external-resources-qds.qdoc +++ b/doc/qtcreator/src/external-resources/external-resources-qds.qdoc @@ -22,8 +22,8 @@ \title \QMCU documentation */ /*! - \externalpage https://doc.qt.io/QtForMCUs/qtul-qsg-traveoii-designui.html - \title Designing a UI for Infineon Traveo II + \externalpage https://doc.qt.io/QtForMCUs/qtul-qsg-traveot2g-designui.html + \title Designing a UI for Infineon Traveo T2G */ /*! \externalpage https://doc.qt.io/QtForMCUs/qtul-qsg-miimxrt1170-designui.html diff --git a/doc/qtdesignstudio/src/mcus/qtdesignstudio-creating-uis-for-mcus.qdoc b/doc/qtdesignstudio/src/mcus/qtdesignstudio-creating-uis-for-mcus.qdoc index 2a02f879ee8..2d11a07de8d 100644 --- a/doc/qtdesignstudio/src/mcus/qtdesignstudio-creating-uis-for-mcus.qdoc +++ b/doc/qtdesignstudio/src/mcus/qtdesignstudio-creating-uis-for-mcus.qdoc @@ -42,7 +42,7 @@ to use \QDS to design a UI for a specific MCU target device, see: \list - \li \l {Designing a UI for Infineon Traveo II} + \li \l {Designing a UI for Infineon Traveo T2G} \li \l {Designing a UI for NXP i.MX RT1170} \li \l {Designing a UI for Renesas RA6M3G} \li \l {Designing a UI for Renesas RH850-D1M1A} From 211ae491308163cdc1c6ce776c9d706792174610 Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Tue, 6 Feb 2024 10:59:17 +0100 Subject: [PATCH 041/176] QmlDesigner: Close PopupDialog on QtDS inactive Close a PopupDialog whenever the main application becomes inactive by e.g. focusing another app. This prevents the PopupDialog being drawn on top of that any other application. Task-number: QDS-11870 Change-Id: I2d5dce4a7a54d43382010dedee3ba214a70d2eb8 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen --- .../imports/StudioControls/PopupDialog.qml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/PopupDialog.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/PopupDialog.qml index 4da26cf4940..acf805928e7 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/PopupDialog.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/PopupDialog.qml @@ -286,8 +286,10 @@ QtObject { enabled: root.visible function onFocusWindowChanged(focusWindow) { - if (!focusWindow) + if (!focusWindow) { + root.close() return + } if (root.keepOpen) return From ea03bfea10b7c2e5dba1c6cdd8f6aa19ba7d2c59 Mon Sep 17 00:00:00 2001 From: Knud Dollereder Date: Mon, 12 Feb 2024 15:37:43 +0100 Subject: [PATCH 042/176] QmlProjectManager: Cleanup cmake generator - Consider qmlModules file like any other qml module - Use ${CMAKE_PROJECT_NAME} when possible - Break up big functions into smaller ones - Reorder arguments for tpl files Change-Id: Ie73cca8596b22adf67eba1019610a00c34976421 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Reviewed-by: Thomas Hartmann --- .../cmakegen/cmakegenerator.cpp | 281 ++++++++++-------- .../cmakegen/cmakegenerator.h | 16 +- .../cmakegen/gencmakemodule.tpl | 8 +- .../cmakegen/gencmakeroot.tpl | 8 +- 4 files changed, 167 insertions(+), 146 deletions(-) diff --git a/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.cpp b/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.cpp index 55bcea6a87d..2d78026125c 100644 --- a/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.cpp +++ b/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.cpp @@ -19,19 +19,16 @@ namespace GenerateCmake { const char TEMPLATE_CMAKELISTS_ROOT[] = ":/boilerplatetemplates/gencmakeroot.tpl"; const char TEMPLATE_CMAKELISTS_MODULE[] = ":/boilerplatetemplates/gencmakemodule.tpl"; -const char TEMPLATE_QMLMODULES[] = ":/boilerplatetemplates/qmlprojectmodules.tpl"; 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"; -const char DO_NOT_EDIT_FILE_COMMENT[] = -"### This file is automatically generated by Qt Design Studio.\n" -"### Do not change\n\n"; +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"; - -const char BIG_RESOURCE_TEMPLATE[] = R"( +const char TEMPLATE_BIG_RESOURCES[] = R"( qt6_add_resources(%1 %2 BIG_RESOURCES PREFIX "%3" @@ -39,6 +36,11 @@ qt6_add_resources(%1 %2 FILES %4 ))"; +const char TEMPLATE_LINK_LIBRARIES[] = R"( +target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE +%3 +))"; + CMakeGenerator::CMakeGenerator(QmlBuildSystem *bs, QObject *parent) : QObject(parent) , m_root(std::make_shared()) @@ -59,7 +61,9 @@ void CMakeGenerator::initialize(QmlProject *project) m_moduleNames.clear(); m_root = std::make_shared(); - m_root->name = QString("Root"); + m_root->module = true; + m_root->uri = QString("Main"); + m_root->name = QString("Main"); m_root->dir = project->rootProjectDirectory(); m_projectName = project->displayName(); @@ -102,66 +106,48 @@ void CMakeGenerator::update(const QSet &added, const QSet &rem createModuleCMakeFile(module); } -std::vector CMakeGenerator::qmlFiles(const NodePtr &node) const +std::vector CMakeGenerator::files(const NodePtr &node, + const FileGetter &getter) const { - std::vector out = node->files; - for (const NodePtr &child : node->subdirs) { + std::vector out = getter(node); + for (const CMakeGenerator::NodePtr &child : node->subdirs) { if (child->module) continue; - auto childFiles = qmlFiles(child); + auto childFiles = files(child, getter); out.insert(out.end(), childFiles.begin(), childFiles.end()); } return out; } +std::vector CMakeGenerator::qmlFiles(const NodePtr &node) const +{ + return files(node, [](const NodePtr &n) { return n->files; }); +} + std::vector CMakeGenerator::singletons(const NodePtr &node) const { - std::vector out = node->singletons; - for (const NodePtr &child : node->subdirs) { - if (child->module) - continue; - - auto childFiles = singletons(child); - out.insert(out.end(), childFiles.begin(), childFiles.end()); - } - return out; + return files(node, [](const NodePtr &n) { return n->singletons; }); } std::vector CMakeGenerator::resources(const NodePtr &node) const { - std::vector out = node->resources; - for (const NodePtr &child : node->subdirs) { - if (child->module) - continue; - - auto childFiles = resources(child); - out.insert(out.end(), childFiles.begin(), childFiles.end()); - } - return out; + return files(node, [](const NodePtr &n) { return n->resources; }); } std::vector CMakeGenerator::sources(const NodePtr &node) const { - std::vector out = node->sources; - for (const NodePtr &child : node->subdirs) { - if (child->module) - continue; - - auto childFiles = sources(child); - out.insert(out.end(), childFiles.begin(), childFiles.end()); - } - return out; + return files(node, [](const NodePtr &n) { return n->sources; }); } void CMakeGenerator::createCMakeFiles(const NodePtr &node) const { - if (node->name == "Root") { + if (isRootNode(node)) createMainCMakeFile(node); - createQmlModuleFile(node); - } else if (node->module || hasChildModule(node)) { + + if (node->module || hasChildModule(node)) createModuleCMakeFile(node); - } + for (const NodePtr &n : node->subdirs) createCMakeFiles(n); } @@ -183,103 +169,43 @@ void CMakeGenerator::createMainCMakeFile(const NodePtr &node) const writeFile(file, fileContent); } -void CMakeGenerator::createQmlModuleFile(const NodePtr &node) const -{ - const QString appName = m_projectName + "App"; - - QString subdirIncludes; - for (const NodePtr &n : node->subdirs) - subdirIncludes.append(QString(ADD_SUBDIR).arg(n->name)); - - QString modulesAsPlugins; - for (const QString &moduleName : m_moduleNames) - modulesAsPlugins.append(" " + moduleName + "plugin\n"); - - const QString fileTemplate = readTemplate(TEMPLATE_QMLMODULES); - const QString fileContent = fileTemplate.arg(appName, subdirIncludes, modulesAsPlugins); - - const Utils::FilePath file = node->dir.pathAppended("qmlModules"); - writeFile(file, fileContent); -} - void CMakeGenerator::createModuleCMakeFile(const NodePtr &node) const { - QString subDirContent; - for (const NodePtr &n : node->subdirs) { - if (n->module || hasChildModule(n)) - subDirContent.append(QString(ADD_SUBDIR).arg(n->dir.fileName())); - } + Utils::FilePath writeToFile = node->dir.pathAppended("CMakeLists.txt"); - QString content; if (!node->module && hasChildModule(node)) { - content.append(DO_NOT_EDIT_FILE_COMMENT); - content.append(subDirContent); - Utils::FilePath file = node->dir.pathAppended("CMakeLists.txt"); - writeFile(file, content); + QString content(DO_NOT_EDIT_FILE_COMMENT); + content.append(makeSubdirectoriesBlock(node)); + writeFile(writeToFile, content); return; } - auto makeRelative = [](const Utils::FilePath &base, - const Utils::FilePath &converted) -> QString { - return "\"" + Utils::FilePath::calcRelativePath(converted.toString(), base.toString()) + "\""; - }; - - QString uri = node->uri; - if (uri.isEmpty()) - uri = node->dir.baseName(); - - const QString setProperties( - "set_source_files_properties(%1\n PROPERTIES\n %2 %3\n)\n\n"); - - for (const Utils::FilePath &path : node->singletons) { - content.append(setProperties.arg(path.fileName()).arg("QT_QML_SINGLETON_TYPE").arg("true")); - } - - if (!subDirContent.isEmpty()) - content.append(subDirContent); - - QString qmlFileContent; - for (const Utils::FilePath &path : qmlFiles(node)) { - qmlFileContent.append(QString(" %1\n").arg(makeRelative(node->dir, path))); - } + QString templatePrefix; + templatePrefix.append(makeSubdirectoriesBlock(node)); + templatePrefix.append(makeSingletonBlock(node)); + auto [resources, bigResources] = makeResourcesBlocks(node); QString moduleContent; - if (!qmlFileContent.isEmpty()) - moduleContent.append(QString(" QML_FILES\n%1").arg(qmlFileContent)); + moduleContent.append(makeQmlFilesBlock(node)); + moduleContent.append(resources); - std::vector bigResources; - QString resourceFiles; - for (const Utils::FilePath &path : resources(node)) { - if (path.fileSize() > 5000000) { - bigResources.push_back(makeRelative(node->dir, path)); - continue; - } - resourceFiles.append(QString(" %1\n").arg(makeRelative(node->dir, path))); - } + QString templatePostfix; + templatePostfix.append(bigResources); - if (!resourceFiles.isEmpty()) - moduleContent.append(QString(" RESOURCES\n%1").arg(resourceFiles)); + if (isRootNode(node)) { + writeToFile = node->dir.pathAppended("qmlModules"); + QString pluginNames; + for (const QString &moduleName : m_moduleNames) + pluginNames.append(" " + moduleName + "plugin\n"); - QString bigResourceContent; - if (!bigResources.empty()) { - QString resourceContent; - for (const QString &res : bigResources) - resourceContent.append(QString("\n %1").arg(res)); - - const QString prefixPath = QString(uri).replace('.', '/'); - const QString prefix = "/qt/qml/" + prefixPath; - const QString resourceName = node->name + "BigResource"; - - bigResourceContent = QString::fromUtf8(BIG_RESOURCE_TEMPLATE, -1) - .arg(node->name, resourceName, prefix, resourceContent); + templatePostfix += QString::fromUtf8(TEMPLATE_LINK_LIBRARIES, -1).arg(pluginNames); } const QString fileTemplate = readTemplate(TEMPLATE_CMAKELISTS_MODULE); - const QString fileContent = - fileTemplate.arg(content, node->name, uri, moduleContent, bigResourceContent); + const QString fileContent + = fileTemplate.arg(node->name, node->uri, templatePrefix, moduleContent, templatePostfix); - const Utils::FilePath file = node->dir.pathAppended("CMakeLists.txt"); - writeFile(file, fileContent); + writeFile(writeToFile, fileContent); } void CMakeGenerator::createEntryPoints(const NodePtr &node) const @@ -295,6 +221,7 @@ void CMakeGenerator::createMainCppFile(const NodePtr &node) const const Utils::FilePath componentsHeaderPath = srcDir.pathAppended( "import_qml_components_plugins.h"); + const QString componentsHeaderContent = readTemplate(TEMPLATE_HEADER_IMPORT_COMPS); writeFile(componentsHeaderPath, componentsHeaderContent); @@ -314,7 +241,8 @@ void CMakeGenerator::createMainCppFile(const NodePtr &node) const value.prepend(":/"); environment.append(QString(" qputenv(\"%1\", \"%2\");\n").arg(key).arg(value)); } - const QString envHeaderContent = readTemplate(TEMPLATE_HEADER_ENVIRONMENT).arg(environment); + const QString envHeaderContent + = readTemplate(TEMPLATE_HEADER_ENVIRONMENT).arg(environment); writeFile(envHeaderPath, envHeaderContent); } } @@ -337,10 +265,15 @@ void CMakeGenerator::writeFile(const Utils::FilePath &path, const QString &conte 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 = {}; - + QString value; if (m_buildSystem) { auto envItems = m_buildSystem->environment(); auto confEnv = std::find_if(envItems.begin(), @@ -352,6 +285,78 @@ QString CMakeGenerator::makeEnvironmentVariable(const QString &key) const 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 CMakeGenerator::makeResourcesBlocks(const NodePtr &node) const +{ + QString resourcesOut; + QString bigResourcesOut; + + QString resourceFiles; + std::vector 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); @@ -384,7 +389,6 @@ void CMakeGenerator::readQmlDir(const Utils::FilePath &filePath, NodePtr &node) node->singletons.push_back(tmp); } } - f.close(); } @@ -470,6 +474,11 @@ 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) { @@ -484,11 +493,20 @@ bool CMakeGenerator::hasChildModule(const NodePtr &node) const 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"}; + "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) + qDebug() << "Module: " << node->name; + + for (const auto &child : node->subdirs) + printModules(child); +} + void CMakeGenerator::printNodeTree(const NodePtr &generatorNode, size_t indent) const { auto addIndent = [](size_t level) -> QString { @@ -516,8 +534,9 @@ void CMakeGenerator::parseNodeTree(NodePtr &generatorNode, if (const auto *subFolderNode = childNode->asFolderNode()) { CMakeGenerator::NodePtr childGeneratorNode = std::make_shared(); childGeneratorNode->parent = generatorNode; - childGeneratorNode->name = subFolderNode->displayName(); childGeneratorNode->dir = subFolderNode->filePath(); + childGeneratorNode->name = subFolderNode->displayName(); + childGeneratorNode->uri = childGeneratorNode->name; parseNodeTree(childGeneratorNode, subFolderNode); generatorNode->subdirs.push_back(childGeneratorNode); } else if (auto *fileNode = childNode->asFileNode()) { diff --git a/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.h b/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.h index 54445d3561d..f89257ac5dc 100644 --- a/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.h +++ b/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.h @@ -17,11 +17,6 @@ class QmlBuildSystem; namespace GenerateCmake { -// TODO: -// - Create "module" for src dir -// - Replace AppName in templates with ${CMAKE_PROJECT_NAME} -// - Introduce Blacklist (designer) - class CMakeGenerator : public QObject { Q_OBJECT @@ -53,15 +48,15 @@ private: }; using NodePtr = std::shared_ptr; + using FileGetter = std::function(const NodePtr &)>; + std::vector files(const NodePtr &node, const FileGetter &getter) const; std::vector qmlFiles(const NodePtr &node) const; std::vector singletons(const NodePtr &node) const; std::vector resources(const NodePtr &node) const; std::vector sources(const NodePtr &node) const; void createCMakeFiles(const NodePtr &node) const; - - void createQmlModuleFile(const NodePtr &node) const; void createMainCMakeFile(const NodePtr &node) const; void createModuleCMakeFile(const NodePtr &node) const; @@ -69,7 +64,12 @@ private: 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 makeResourcesBlocks(const NodePtr &node) const; QString readTemplate(const QString &templatePath) const; void readQmlDir(const Utils::FilePath &filePath, NodePtr &node) const; @@ -81,9 +81,11 @@ private: 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); diff --git a/src/plugins/qmlprojectmanager/cmakegen/gencmakemodule.tpl b/src/plugins/qmlprojectmanager/cmakegen/gencmakemodule.tpl index 7c4b38b0433..b81d253c907 100644 --- a/src/plugins/qmlprojectmanager/cmakegen/gencmakemodule.tpl +++ b/src/plugins/qmlprojectmanager/cmakegen/gencmakemodule.tpl @@ -1,11 +1,11 @@ ### This file is automatically generated by Qt Design Studio. ### Do not change -%1 +%3 -qt_add_library(%2 STATIC) -qt6_add_qml_module(%2 - URI "%3" +qt_add_library(%1 STATIC) +qt6_add_qml_module(%1 + URI "%2" VERSION 1.0 RESOURCE_PREFIX "/qt/qml" %4 diff --git a/src/plugins/qmlprojectmanager/cmakegen/gencmakeroot.tpl b/src/plugins/qmlprojectmanager/cmakegen/gencmakeroot.tpl index 0b8553d1958..a23a311c1a9 100644 --- a/src/plugins/qmlprojectmanager/cmakegen/gencmakeroot.tpl +++ b/src/plugins/qmlprojectmanager/cmakegen/gencmakeroot.tpl @@ -19,14 +19,14 @@ if (Qt6_VERSION VERSION_GREATER_EQUAL 6.3) qt_standard_project_setup() endif() -qt_add_executable(%1 %2) +qt_add_executable(${CMAKE_PROJECT_NAME} %2) -qt_add_resources(%1 "configuration" +qt_add_resources(${CMAKE_PROJECT_NAME} "configuration" PREFIX "/" %3 ) -target_link_libraries(%1 PRIVATE +target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Gui Qt${QT_VERSION_MAJOR}::Quick @@ -44,7 +44,7 @@ if (LINK_INSIGHT) endif () include(GNUInstallDirs) -install(TARGETS %1 +install(TARGETS ${CMAKE_PROJECT_NAME} BUNDLE DESTINATION . LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} From bf5d094ef662551be6d79bba2632c62dc0493276 Mon Sep 17 00:00:00 2001 From: Burak Hancerli Date: Wed, 14 Feb 2024 10:55:00 +0100 Subject: [PATCH 043/176] GenerateQmlrc: Do not create package if user cancels the operation Change-Id: Ie8f2bed23d6c9b4c8496b9b75310fc8cda823088 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Thomas Hartmann --- src/plugins/qmldesigner/generateresource.cpp | 22 +++++++++++++------- src/plugins/qmldesigner/generateresource.h | 2 +- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/plugins/qmldesigner/generateresource.cpp b/src/plugins/qmldesigner/generateresource.cpp index ebd96c45a33..44a47e3033f 100644 --- a/src/plugins/qmldesigner/generateresource.cpp +++ b/src/plugins/qmldesigner/generateresource.cpp @@ -77,9 +77,9 @@ QTableWidget* GenerateResource::createFilesTable(const QList &file return table; } -QStringList GenerateResource::getFileList(const QList &fileNames) +std::optional GenerateResource::getFileList(const QList &fileNames) { - QStringList result; + std::optional result; QDialog *dialog = new QDialog(Core::ICore::dialogParent()); dialog->setMinimumWidth(480); dialog->setMinimumHeight(640); @@ -118,7 +118,7 @@ QStringList GenerateResource::getFileList(const QList &fileNames) } } - result = fileList; + result.emplace(fileList); }); dialog->exec(); @@ -311,7 +311,10 @@ void GenerateResource::generateMenuEntry(QObject *parent) temp.close(); - QStringList modifiedList = getFileList(fileList); + std::optional modifiedList = getFileList(fileList); + + if (!modifiedList) + return; if (!persistentFile.open(QIODevice::ReadWrite | QIODevice::Truncate)) return; @@ -323,7 +326,7 @@ void GenerateResource::generateMenuEntry(QObject *parent) persistentFile.write(firstLine.trimmed()); writer.writeStartElement("qresource"); - for (QString file : modifiedList) + for (const QString &file : modifiedList.value()) writer.writeTextElement("file", file.trimmed()); writer.writeEndElement(); @@ -446,7 +449,11 @@ void GenerateResource::generateMenuEntry(QObject *parent) temp.close(); persistentFile.close(); - QStringList modifiedList = getFileList(fileList); + std::optional modifiedList = getFileList(fileList); + + if (!modifiedList) + return; + QTemporaryFile tempFile(projectPath.toString() + "/XXXXXXX.create.modifiedresource.qrc"); if (!tempFile.open()) @@ -459,7 +466,7 @@ void GenerateResource::generateMenuEntry(QObject *parent) tempFile.write(firstLine.trimmed()); writer.writeStartElement("qresource"); - for (QString file : modifiedList) + for (const QString &file : modifiedList.value()) writer.writeTextElement("file", file.trimmed()); writer.writeEndElement(); @@ -486,4 +493,3 @@ void GenerateResource::generateMenuEntry(QObject *parent) } } // namespace QmlDesigner - diff --git a/src/plugins/qmldesigner/generateresource.h b/src/plugins/qmldesigner/generateresource.h index 029283f7ef4..cc0c0147a90 100644 --- a/src/plugins/qmldesigner/generateresource.h +++ b/src/plugins/qmldesigner/generateresource.h @@ -12,7 +12,7 @@ struct ResourceFile bool inProject; }; void generateMenuEntry(QObject *parent); - QStringList getFileList(const QList &); + std::optional getFileList(const QList &); QTableWidget* createFilesTable(const QList &); } } // namespace QmlDesigner From e4fe45f237516fcce24cd77651b9493e33bf39ad Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Wed, 14 Feb 2024 17:48:53 +0200 Subject: [PATCH 044/176] QmlDesigner: Add beta tag to the title of the Model Editor view Change-Id: I2eff47c56928a6e49405b646ff7a98b205d9d6fe Reviewed-by: Mahmoud Badri --- .../qmldesigner/components/collectioneditor/collectionview.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp index 143612bb339..9933ac38179 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp @@ -112,7 +112,7 @@ QmlDesigner::WidgetInfo CollectionView::widgetInfo() "CollectionEditor", WidgetInfo::LeftPane, 0, - tr("Model Editor"), + tr("Model Editor [beta]"), tr("Model Editor view")); } From 6996687392029afe8927b8a4cdf174b44778ebb9 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Wed, 14 Feb 2024 20:12:11 +0200 Subject: [PATCH 045/176] QmlDesigner: Keep the order of the columns in imported CSV Fixes: QDS-11667 Change-Id: I5f4bba2224878f404e333425502f084c589be99c Reviewed-by: Reviewed-by: Miikka Heikkinen Reviewed-by: Qt CI Patch Build Bot --- .../components/collectioneditor/collectiondetails.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp index 683d4112eb3..c4c7972582b 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp @@ -733,7 +733,7 @@ CollectionDetails CollectionDetails::fromImportedCsv(const QByteArray &document) } } - return fromImportedJson(importedArray); + return fromImportedJson(importedArray, headers); } CollectionDetails CollectionDetails::fromImportedJson(const QByteArray &json, QJsonParseError *error) From 4c79e1b8d82bfb32aafa20894ab6a060231467d5 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 15 Feb 2024 10:43:33 +0100 Subject: [PATCH 046/176] QmlDesigner: Correct import versions for Qt 5 Task-number: QDS-11898 Change-Id: I4e59e31f9396ab957eac5effbf17fd6edc930798 Reviewed-by: Marco Bubke --- .../designercore/model/texttomodelmerger.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp index 9ab009bd830..6e2b425fa76 100644 --- a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp +++ b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp @@ -917,13 +917,13 @@ QList generatePossibleFileImports(const QString &path, QmlDesigner::Imports createQt5Modules() { - return {QmlDesigner::Import::createLibraryImport("QtQuick", "5.15"), - QmlDesigner::Import::createLibraryImport("QtQuick.Controls", "5.15"), - QmlDesigner::Import::createLibraryImport("QtQuick.Window", "5.15"), - QmlDesigner::Import::createLibraryImport("QtQuick.Layouts", "5.15"), - QmlDesigner::Import::createLibraryImport("QtQuick.Timeline", "5.15"), - QmlDesigner::Import::createLibraryImport("QtCharts", "5.15"), - QmlDesigner::Import::createLibraryImport("QtDataVisulaization", "5.15"), + return {QmlDesigner::Import::createLibraryImport("QtQuick", "2.15"), + QmlDesigner::Import::createLibraryImport("QtQuick.Controls", "2.15"), + QmlDesigner::Import::createLibraryImport("QtQuick.Window", "2.15"), + QmlDesigner::Import::createLibraryImport("QtQuick.Layouts", "2.15"), + QmlDesigner::Import::createLibraryImport("QtQuick.Timeline", "1.0"), + QmlDesigner::Import::createLibraryImport("QtCharts", "2.15"), + QmlDesigner::Import::createLibraryImport("QtDataVisulaization", "2.15"), QmlDesigner::Import::createLibraryImport("QtQuick.Studio.Controls", "1.0"), QmlDesigner::Import::createLibraryImport("QtQuick.Studio.Effects", "1.0"), QmlDesigner::Import::createLibraryImport("FlowView", "1.0"), From efe114872c543f997a063d52cd48db0b4d3f5aef Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Wed, 14 Feb 2024 15:39:33 +0200 Subject: [PATCH 047/176] QmlDesigner: Use FileSaver instead of file in Model Editor * Also the warning is removed for internal changes on local files Fixes: QDS-11903 Change-Id: Ic35966888433a2bd9b1cc10a47bb7ed51280ffb1 Reviewed-by: Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Shrief Gabr Reviewed-by: Miikka Heikkinen Reviewed-by: Mahmoud Badri --- .../collectiondetailsmodel.cpp | 5 +- .../collectioneditorutils.cpp | 13 +++ .../collectioneditor/collectioneditorutils.h | 4 + .../collectionsourcemodel.cpp | 75 +++++++-------- .../collectioneditor/collectionwidget.cpp | 93 +++---------------- .../collectioneditor/collectionwidget.h | 4 - .../collectioneditor/datastoremodelnode.cpp | 2 + 7 files changed, 65 insertions(+), 131 deletions(-) diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp index 17a26c58c5c..e7a246b68e7 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp @@ -414,7 +414,6 @@ bool CollectionDetailsModel::saveDataStoreCollections() const ModelNode node = m_currentCollection.reference().node; const Utils::FilePath path = CollectionEditorUtils::dataStoreJsonFilePath(); Utils::FileReader fileData; - Utils::FileSaver sourceFile(path); if (!fileData.fetch(path)) { qWarning() << Q_FUNC_INFO << "Cannot read the json file:" << fileData.errorString(); @@ -437,10 +436,8 @@ bool CollectionDetailsModel::saveDataStoreCollections() } document.setObject(obj); - bool saved = sourceFile.write(document.toJson()); - saved &= sourceFile.finalize(); - if (saved) { + if (CollectionEditorUtils::writeToJsonDocument(path, document)) { const CollectionReference currentReference = m_currentCollection.reference(); for (CollectionDetails &collection : collectionsToBeSaved) { collection.markSaved(); diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp index 9c777249323..d9bbc764156 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp @@ -7,6 +7,7 @@ #include "nodemetainfo.h" #include "propertymetainfo.h" +#include #include #include #include @@ -393,4 +394,16 @@ QStringList dataTypesStringList() return typesList; } +bool writeToJsonDocument(const Utils::FilePath &path, const QJsonDocument &document, QString *errorString) +{ + Core::FileChangeBlocker fileBlocker(path); + Utils::FileSaver jsonFile(path); + if (jsonFile.write(document.toJson())) + jsonFile.finalize(); + if (errorString) + *errorString = jsonFile.errorString(); + + return !jsonFile.hasError(); +} + } // namespace QmlDesigner::CollectionEditorUtils diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h index 5f59329fc62..35a036c4e32 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h @@ -29,6 +29,10 @@ Utils::FilePath dataStoreJsonFilePath(); Utils::FilePath dataStoreQmlFilePath(); +bool writeToJsonDocument(const Utils::FilePath &path, + const QJsonDocument &document, + QString *errorString = nullptr); + bool isDataStoreNode(const ModelNode &dataStoreNode); bool ensureDataStoreExists(bool &justCreated); diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.cpp index 9378d6871c6..a76d4a0b24e 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.cpp @@ -9,6 +9,7 @@ #include "collectionlistmodel.h" #include "variantproperty.h" +#include #include #include @@ -27,6 +28,8 @@ QSharedPointer loadCollection( { using namespace QmlDesigner::CollectionEditorConstants; using namespace QmlDesigner::CollectionEditorUtils; + using Utils::FilePath; + using Utils::FileReader; QString sourceFileAddress = getSourceCollectionPath(sourceNode); QSharedPointer collectionsList; @@ -40,12 +43,12 @@ QSharedPointer loadCollection( }; if (sourceNode.type() == JSONCOLLECTIONMODEL_TYPENAME) { - QFile sourceFile(sourceFileAddress); - if (!sourceFile.open(QFile::ReadOnly)) + FileReader sourceFile; + if (!sourceFile.fetch(FilePath::fromUserInput(sourceFileAddress))) return {}; QJsonParseError parseError; - QJsonDocument document = QJsonDocument::fromJson(sourceFile.readAll(), &parseError); + QJsonDocument document = QJsonDocument::fromJson(sourceFile.data(), &parseError); if (parseError.error != QJsonParseError::NoError) return {}; @@ -273,6 +276,8 @@ bool CollectionSourceModel::addCollectionToSource(const ModelNode &node, const QJsonObject &newCollection, QString *errorString) { + using Utils::FilePath; + using Utils::FileReader; auto returnError = [errorString](const QString &msg) -> bool { if (errorString) *errorString = msg; @@ -295,13 +300,15 @@ bool CollectionSourceModel::addCollectionToSource(const ModelNode &node, if (!sourceFileInfo.isFile()) return returnError(tr("Selected node must have a valid source file address")); - QFile jsonFile(sourceFileAddress); - if (!jsonFile.open(QFile::ReadWrite)) - return returnError(tr("Can't read or write \"%1\".\n%2") - .arg(sourceFileInfo.absoluteFilePath(), jsonFile.errorString())); + FilePath jsonPath = FilePath::fromUserInput(sourceFileAddress); + FileReader jsonFile; + if (!jsonFile.fetch(jsonPath)) { + return returnError( + tr("Can't read \"%1\".\n%2").arg(sourceFileInfo.absoluteFilePath(), jsonFile.errorString())); + } QJsonParseError parseError; - QJsonDocument document = QJsonDocument::fromJson(jsonFile.readAll(), &parseError); + QJsonDocument document = QJsonDocument::fromJson(jsonFile.data(), &parseError); if (parseError.error != QJsonParseError::NoError) return returnError(tr("\"%1\" is corrupted.\n%2") .arg(sourceFileInfo.absoluteFilePath(), parseError.errorString())); @@ -310,14 +317,8 @@ bool CollectionSourceModel::addCollectionToSource(const ModelNode &node, QJsonObject sourceObject = document.object(); sourceObject.insert(collectionName, newCollection); document.setObject(sourceObject); - if (!jsonFile.resize(0)) - return returnError(tr("Can't clean \"%1\".").arg(sourceFileInfo.absoluteFilePath())); - QByteArray jsonData = document.toJson(); - auto writtenBytes = jsonFile.write(jsonData); - jsonFile.close(); - - if (writtenBytes != jsonData.size()) + if (!CollectionEditorUtils::writeToJsonDocument(jsonPath, document)) return returnError(tr("Can't write to \"%1\".").arg(sourceFileInfo.absoluteFilePath())); updateCollectionList(index(idx)); @@ -417,6 +418,10 @@ void CollectionSourceModel::onSelectedCollectionChanged(CollectionListModel *col void CollectionSourceModel::onCollectionNameChanged(CollectionListModel *collectionList, const QString &oldName, const QString &newName) { + using Utils::FilePath; + using Utils::FileReader; + using Utils::FileSaver; + auto emitRenameWarning = [this](const QString &msg) -> void { emit warning(tr("Rename Model"), msg); }; @@ -446,15 +451,16 @@ void CollectionSourceModel::onCollectionNameChanged(CollectionListModel *collect return; } - QFile jsonFile(sourceFileAddress); - if (!jsonFile.open(QFile::ReadWrite)) { - emitRenameWarning(tr("Can't read or write \"%1\".\n%2") - .arg(sourceFileInfo.absoluteFilePath(), jsonFile.errorString())); + FilePath jsonPath = FilePath::fromUserInput(sourceFileAddress); + FileReader jsonFile; + if (!jsonFile.fetch(jsonPath)) { + emitRenameWarning( + tr("Can't read \"%1\".\n%2").arg(sourceFileInfo.absoluteFilePath(), jsonFile.errorString())); return; } QJsonParseError parseError; - QJsonDocument document = QJsonDocument::fromJson(jsonFile.readAll(), &parseError); + QJsonDocument document = QJsonDocument::fromJson(jsonFile.data(), &parseError); if (parseError.error != QJsonParseError::NoError) { emitRenameWarning(tr("\"%1\" is corrupted.\n%2") .arg(sourceFileInfo.absoluteFilePath(), parseError.errorString())); @@ -484,16 +490,8 @@ void CollectionSourceModel::onCollectionNameChanged(CollectionListModel *collect rootObject.remove(oldName); document.setObject(rootObject); - if (!jsonFile.resize(0)) { - emitRenameWarning(tr("Can't clean \"%1\".").arg(sourceFileInfo.absoluteFilePath())); - return; - } - QByteArray jsonData = document.toJson(); - auto writtenBytes = jsonFile.write(jsonData); - jsonFile.close(); - - if (writtenBytes != jsonData.size()) { + if (!CollectionEditorUtils::writeToJsonDocument(jsonPath, document)) { emitRenameWarning(tr("Can't write to \"%1\".").arg(sourceFileInfo.absoluteFilePath())); return; } @@ -519,6 +517,8 @@ void CollectionSourceModel::onCollectionNameChanged(CollectionListModel *collect void CollectionSourceModel::onCollectionsRemoved(CollectionListModel *collectionList, const QStringList &removedCollections) { + using Utils::FilePath; + using Utils::FileReader; auto emitDeleteWarning = [this](const QString &msg) -> void { emit warning(tr("Delete Model"), msg); }; @@ -547,15 +547,16 @@ void CollectionSourceModel::onCollectionsRemoved(CollectionListModel *collection return; } - QFile jsonFile(sourceFileAddress); - if (!jsonFile.open(QFile::ReadWrite)) { + FilePath jsonPath = FilePath::fromUserInput(sourceFileAddress); + FileReader jsonFile; + if (!jsonFile.fetch(jsonPath)) { emitDeleteWarning(tr("Can't read or write \"%1\".\n%2") .arg(sourceFileInfo.absoluteFilePath(), jsonFile.errorString())); return; } QJsonParseError parseError; - QJsonDocument document = QJsonDocument::fromJson(jsonFile.readAll(), &parseError); + QJsonDocument document = QJsonDocument::fromJson(jsonFile.data(), &parseError); if (parseError.error != QJsonParseError::NoError) { emitDeleteWarning(tr("\"%1\" is corrupted.\n%2") .arg(sourceFileInfo.absoluteFilePath(), parseError.errorString())); @@ -578,16 +579,8 @@ void CollectionSourceModel::onCollectionsRemoved(CollectionListModel *collection } document.setObject(rootObject); - if (!jsonFile.resize(0)) { - emitDeleteWarning(tr("Can't clean \"%1\".").arg(sourceFileInfo.absoluteFilePath())); - return; - } - QByteArray jsonData = document.toJson(); - auto writtenBytes = jsonFile.write(jsonData); - jsonFile.close(); - - if (writtenBytes != jsonData.size()) { + if (!CollectionEditorUtils::writeToJsonDocument(jsonPath, document)) { emitDeleteWarning(tr("Can't write to \"%1\".").arg(sourceFileInfo.absoluteFilePath())); return; } diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp index 24fb745ab77..79d3d563a25 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp @@ -17,7 +17,6 @@ #include #include -#include #include #include #include @@ -167,14 +166,14 @@ bool CollectionWidget::loadCsvFile(const QUrl &url, const QString &collectionNam bool CollectionWidget::isJsonFile(const QUrl &url) const { - QString filePath = url.isLocalFile() ? url.toLocalFile() : url.toString(); - QFile file(filePath); - - if (!file.exists() || !file.open(QFile::ReadOnly)) + Utils::FilePath filePath = Utils::FilePath::fromUserInput(url.isLocalFile() ? url.toLocalFile() + : url.toString()); + Utils::FileReader file; + if (!file.fetch(filePath)) return false; QJsonParseError error; - QJsonDocument::fromJson(file.readAll(), &error); + QJsonDocument::fromJson(file.data(), &error); if (error.error) return false; @@ -184,9 +183,8 @@ bool CollectionWidget::isJsonFile(const QUrl &url) const bool CollectionWidget::isCsvFile(const QUrl &url) const { QString filePath = url.isLocalFile() ? url.toLocalFile() : url.toString(); - QFile file(filePath); - - return file.exists() && file.fileName().endsWith(".csv"); + QFileInfo fileInfo(filePath); + return fileInfo.exists() && !fileInfo.suffix().compare("csv", Qt::CaseInsensitive); } bool CollectionWidget::isValidUrlToImport(const QUrl &url) const @@ -203,73 +201,6 @@ bool CollectionWidget::isValidUrlToImport(const QUrl &url) const return false; } -bool CollectionWidget::addCollection(const QString &collectionName, - const QString &collectionType, - const QUrl &sourceUrl, - const QVariant &sourceNode) -{ - const ModelNode node = sourceNode.value(); - bool isNewCollection = !node.isValid(); - - if (isNewCollection) { - QString sourcePath = sourceUrl.isLocalFile() ? sourceUrl.toLocalFile() : sourceUrl.toString(); - - if (collectionType == "json") { - QJsonObject jsonObject; - jsonObject.insert(collectionName, CollectionEditorUtils::defaultCollection()); - - QFile sourceFile(sourcePath); - if (!sourceFile.open(QFile::WriteOnly)) { - warn(tr("File error"), - tr("Can not open the file to write.\n") + sourceFile.errorString()); - return false; - } - - sourceFile.write(QJsonDocument(jsonObject).toJson()); - sourceFile.close(); - - bool loaded = loadJsonFile(sourcePath, collectionName); - if (!loaded) - sourceFile.remove(); - - return loaded; - } else if (collectionType == "csv") { - QFile sourceFile(sourcePath); - if (!sourceFile.open(QFile::WriteOnly)) { - warn(tr("File error"), - tr("Can not open the file to write.\n") + sourceFile.errorString()); - return false; - } - - sourceFile.write("Column1\n\n"); - sourceFile.close(); - - bool loaded = loadCsvFile(sourcePath, collectionName); - if (!loaded) - sourceFile.remove(); - - return loaded; - } else if (collectionType == "existing") { - QFileInfo fileInfo(sourcePath); - if (fileInfo.suffix() == "json") - return loadJsonFile(sourcePath, collectionName); - else if (fileInfo.suffix() == "csv") - return loadCsvFile(sourcePath, collectionName); - } - } else if (collectionType == "json") { - QString errorMsg; - bool added = m_sourceModel->addCollectionToSource(node, - collectionName, - CollectionEditorUtils::defaultCollection(), - &errorMsg); - if (!added) - warn(tr("Can not add a model to the JSON file"), errorMsg); - return added; - } - - return false; -} - bool CollectionWidget::importFile(const QString &collectionName, const QUrl &url) { using Utils::FilePath; @@ -289,15 +220,13 @@ bool CollectionWidget::importFile(const QString &collectionName, const QUrl &url QByteArray fileContent; auto loadUrlContent = [&]() -> bool { - QFile file(url.isLocalFile() ? url.toLocalFile() : url.toString()); - - if (file.open(QFile::ReadOnly)) { - fileContent = file.readAll(); - file.close(); + Utils::FileReader file; + if (file.fetch(fileInfo)) { + fileContent = file.data(); return true; } - warn(tr("Import from file"), tr("Cannot import from file \"%1\"").arg(file.fileName())); + warn(tr("Import from file"), tr("Cannot import from file \"%1\"").arg(fileInfo.fileName())); return false; }; diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h index 5a9c45d591d..23f4ad8dbf3 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h @@ -39,10 +39,6 @@ public: Q_INVOKABLE bool isJsonFile(const QUrl &url) const; Q_INVOKABLE bool isCsvFile(const QUrl &url) const; Q_INVOKABLE bool isValidUrlToImport(const QUrl &url) const; - Q_INVOKABLE bool addCollection(const QString &collectionName, - const QString &collectionType, - const QUrl &sourceUrl, - const QVariant &sourceNode); Q_INVOKABLE bool importFile(const QString &collectionName, const QUrl &url); Q_INVOKABLE bool addCollectionToDataStore(const QString &collectionName); diff --git a/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.cpp b/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.cpp index b85c6517857..e5ddb1dd3a0 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.cpp @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -301,6 +302,7 @@ void DataStoreModelNode::updateSingletonFile() imports += QStringLiteral("import %1\n").arg(import.toString(true)); QString content = pragmaSingleTone + imports + getModelQmlText(); + Core::DocumentManager::expectFileChange(dataStoreQmlFilePath()); FileSaver file(dataStoreQmlFilePath()); file.write(content.toLatin1()); file.finalize(); From 7a73b7a6b127f722b1d99eea9c69eec4c731fc0f Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Tue, 13 Feb 2024 16:37:22 +0100 Subject: [PATCH 048/176] Remove entries with default value Change-Id: I1f78c85a5a8d2a6e22d7bc3537ecb13018f4a661 Reviewed-by: Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen --- src/plugins/qmldesigner/.clang-format | 90 +-------------------------- tests/unit/.clang-format | 90 +-------------------------- 2 files changed, 2 insertions(+), 178 deletions(-) diff --git a/src/plugins/qmldesigner/.clang-format b/src/plugins/qmldesigner/.clang-format index 87648a20579..9986740c63e 100644 --- a/src/plugins/qmldesigner/.clang-format +++ b/src/plugins/qmldesigner/.clang-format @@ -1,92 +1,29 @@ Language: Cpp AccessModifierOffset: -4 -AlignAfterOpenBracket: Align -AlignConsecutiveAssignments: false -AlignConsecutiveDeclarations: false AlignEscapedNewlines: DontAlign -AlignOperands: true -AlignTrailingComments: true -AllowAllArgumentsOnNextLine: true -AllowAllParametersOfDeclarationOnNextLine: true -AllowShortBlocksOnASingleLine: false -AllowShortCaseLabelsOnASingleLine: false AllowShortFunctionsOnASingleLine: Inline -AllowShortLambdasOnASingleLine: All -AllowShortIfStatementsOnASingleLine: false -AllowShortLoopsOnASingleLine: false -AlwaysBreakAfterReturnType: None -AlwaysBreakBeforeMultilineStrings: false -AlwaysBreakTemplateDeclarations: true BinPackArguments: false BinPackParameters: false BraceWrapping: AfterClass: true - AfterControlStatement: false - AfterEnum: false AfterFunction: true - AfterNamespace: false - AfterObjCDeclaration: false AfterStruct: true - AfterUnion: false - BeforeCatch: false - BeforeElse: false - IndentBraces: false SplitEmptyFunction: false SplitEmptyRecord: false SplitEmptyNamespace: false -BreakAfterAttributes: Never BreakBeforeBinaryOperators: All BreakBeforeBraces: Custom -BreakBeforeConceptDeclarations: Always -BreakBeforeInheritanceComma: false -BreakBeforeTernaryOperators: true -BreakConstructorInitializersBeforeComma: false BreakConstructorInitializers: BeforeComma -BreakAfterJavaFieldAnnotations: false BreakInheritanceList: AfterComma -BreakStringLiterals: true ColumnLimit: 100 -CommentPragmas: '^ IWYU pragma:' -CompactNamespaces: false -ConstructorInitializerAllOnOneLineOrOnePerLine: false -ConstructorInitializerIndentWidth: 4 -ContinuationIndentWidth: 4 -Cpp11BracedListStyle: true -DerivePointerAlignment: false -DisableFormat: false -EmptyLineAfterAccessModifier: Never -EmptyLineBeforeAccessModifier: LogicalBlock -ExperimentalAutoDetectBinPacking: false -FixNamespaceComments: true -ForEachMacros: - - forever # avoids { wrapped to next line - - foreach - - Q_FOREACH - - BOOST_FOREACH -#IncludeBlocks: Regroup IncludeCategories: - Regex: '^ Date: Tue, 13 Feb 2024 16:49:18 +0100 Subject: [PATCH 049/176] Nanotrace: Remove global tracer It creates a tracing file and is not used anyway. So we remove the code. Fixes: QTCREATORBUG-30331 Change-Id: I5b62a9d428d10b248821ae76d45221f84f407d3a Reviewed-by: Reviewed-by: Eike Ziller Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen --- src/libs/nanotrace/CMakeLists.txt | 7 +-- src/libs/nanotrace/nanotracehr.cpp | 12 ---- src/libs/nanotrace/nanotracehr.h | 62 ------------------- src/plugins/qmldesigner/CMakeLists.txt | 4 +- .../include/imagecacheauxiliarydata.h | 2 +- .../projectstorage/projectstorage.h | 2 +- .../designercore/tracing/qmldesignertracing.h | 4 +- 7 files changed, 6 insertions(+), 87 deletions(-) diff --git a/src/libs/nanotrace/CMakeLists.txt b/src/libs/nanotrace/CMakeLists.txt index 8652a817985..f2fa830e2d3 100644 --- a/src/libs/nanotrace/CMakeLists.txt +++ b/src/libs/nanotrace/CMakeLists.txt @@ -3,6 +3,7 @@ add_qtc_library(Nanotrace SOURCES nanotraceglobals.h nanotrace.cpp nanotrace.h + nanotracehr.cpp nanotracehr.h PUBLIC_DEPENDS Qt::Core Qt::Gui PROPERTIES CXX_VISIBILITY_PRESET default @@ -15,9 +16,3 @@ extend_qtc_library(Nanotrace CONDITION DESIGN_STUDIO_USE_NANOTRACE PUBLIC_DEFINES NANOTRACE_DESIGNSTUDIO_ENABLED ) - -option(NANOTRACEHR_ENABLED "Enables collecting high resolution performance data" OFF) -extend_qtc_library(Nanotrace - SOURCES - nanotracehr.cpp nanotracehr.h -) diff --git a/src/libs/nanotrace/nanotracehr.cpp b/src/libs/nanotrace/nanotracehr.cpp index 3f02bd527d3..0ccfac594c2 100644 --- a/src/libs/nanotrace/nanotracehr.cpp +++ b/src/libs/nanotrace/nanotracehr.cpp @@ -222,18 +222,6 @@ template NANOTRACE_EXPORT void flushInThread(EnabledEventQueue template NANOTRACE_EXPORT void flushInThread( EnabledEventQueue &eventQueue); -namespace { -TraceFile globalTraceFile{"global.json"}; -thread_local EventQueueData globalEventQueueData{ - globalTraceFile}; -thread_local EventQueue s_globalEventQueue = globalEventQueueData.createEventQueue(); -} // namespace - -EventQueue &globalEventQueue() -{ - return s_globalEventQueue; -} - template EventQueue::EventQueue(EnabledTraceFile *file, TraceEventsSpan eventsOne, diff --git a/src/libs/nanotrace/nanotracehr.h b/src/libs/nanotrace/nanotracehr.h index 74b1381b064..3241d3ded81 100644 --- a/src/libs/nanotrace/nanotracehr.h +++ b/src/libs/nanotrace/nanotracehr.h @@ -32,15 +32,6 @@ static_assert(std::is_same_v, enum class Tracing { IsDisabled, IsEnabled }; -constexpr Tracing tracingStatus() -{ -#ifdef NANOTRACEHR_ENABLED - return Tracing::IsEnabled; -#else - return Tracing::IsDisabled; -#endif -} - #if __cplusplus >= 202002L && __has_cpp_attribute(msvc::no_unique_address) # define NO_UNIQUE_ADDRESS [[msvc::no_unique_address]] #elif __cplusplus >= 202002L && __has_cpp_attribute(no_unique_address) >= 201803L @@ -235,7 +226,6 @@ void convertToString(String &string, const Container &container) template String toArguments(Arguments &&...arguments) { - if constexpr (tracingStatus() == Tracing::IsEnabled) { String text; constexpr auto argumentCount = sizeof...(Arguments); text.append("{"); @@ -246,9 +236,6 @@ String toArguments(Arguments &&...arguments) text.append("}"); return text; - } else { - return {}; - } } inline std::string_view toArguments(std::string_view arguments) @@ -533,8 +520,6 @@ public: TraceEvents eventsTwo; }; -NANOTRACE_EXPORT EventQueue &globalEventQueue(); - template TraceEvent &getTraceEvent(EnabledEventQueue &eventQueue) { @@ -1569,51 +1554,4 @@ template Tracer(typename Category::ArgumentType name, Category &category, Arguments &&...) -> Tracer; -#ifdef NANOTRACEHR_ENABLED -class GlobalTracer -{ -public: - template - [[nodiscard]] GlobalTracer(std::string name, std::string category, Arguments &&...arguments) - : m_name{std::move(name)} - , m_category{std::move(category)} - { - if (globalEventQueue().isEnabled == IsEnabled::Yes) { - Internal::appendArguments(m_arguments, std::forward(arguments)...); - m_start = Clock::now(); - } - } - - ~GlobalTracer() - { - if (globalEventQueue().isEnabled == IsEnabled::Yes) { - auto duration = Clock::now() - m_start; - auto &traceEvent = getTraceEvent(globalEventQueue()); - traceEvent.name = std::move(m_name); - traceEvent.category = std::move(m_category); - traceEvent.arguments = std::move(m_arguments); - traceEvent.time = std::move(m_start); - traceEvent.duration = std::move(duration); - traceEvent.type = 'X'; - } - } - -private: - TimePoint m_start; - std::string m_name; - std::string m_category; - std::string m_arguments; -}; -#else -class GlobalTracer -{ -public: - GlobalTracer(std::string_view, std::string_view, std::string_view) {} - - GlobalTracer(std::string_view, std::string_view) {} - - ~GlobalTracer() {} -}; -#endif - } // namespace NanotraceHR diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 7bfea271e5d..cab1941f7bd 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -27,8 +27,6 @@ env_with_default("QTC_ENABLE_MODEL_TRACING" ENV_QTC_ENABLE_MODEL_TRACING OFF) option(ENABLE_MODEL_TRACING "Enable model tracing" ${ENV_QTC_ENABLE_MODEL_TRACING}) add_feature_info("Model tracing" ${ENABLE_MODEL_TRACING} "") - - add_qtc_library(QmlDesignerUtils STATIC DEPENDS Qt::Gui Utils Qt::QmlPrivate Core @@ -94,7 +92,7 @@ add_qtc_library(QmlDesignerCore STATIC ) extend_qtc_library(QmlDesignerCore - CONDITION TARGET Nanotrace + CONDITION ENABLE_PROJECT_STORAGE_TRACING OR ENABLE_IMAGE_CACHE_TRACING OR ENABLE_MODEL_TRACING PUBLIC_DEPENDS Nanotrace PUBLIC_DEFINES ENABLE_QMLDESIGNER_TRACING diff --git a/src/plugins/qmldesigner/designercore/include/imagecacheauxiliarydata.h b/src/plugins/qmldesigner/designercore/include/imagecacheauxiliarydata.h index a96082ee0fe..f9461907674 100644 --- a/src/plugins/qmldesigner/designercore/include/imagecacheauxiliarydata.h +++ b/src/plugins/qmldesigner/designercore/include/imagecacheauxiliarydata.h @@ -20,7 +20,7 @@ namespace ImageCache { constexpr NanotraceHR::Tracing tracingStatus() { #ifdef ENABLE_IMAGE_CACHE_TRACING - return NanotraceHR::tracingStatus(); + return NanotraceHR::Tracing::IsEnabled; #else return NanotraceHR::Tracing::IsDisabled; #endif diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h index 6244977ec79..91a3412a9f6 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h @@ -31,7 +31,7 @@ using namespace NanotraceHR::Literals; constexpr NanotraceHR::Tracing projectStorageTracingStatus() { #ifdef ENABLE_PROJECT_STORAGE_TRACING - return NanotraceHR::tracingStatus(); + return NanotraceHR::Tracing::IsEnabled; #else return NanotraceHR::Tracing::IsDisabled; #endif diff --git a/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.h b/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.h index fb3f23d2f14..704497f1b7e 100644 --- a/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.h +++ b/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.h @@ -13,7 +13,7 @@ namespace Tracing { constexpr NanotraceHR::Tracing tracingStatus() { #ifdef ENABLE_QMLDESIGNER_TRACING - return NanotraceHR::tracingStatus(); + return NanotraceHR::Tracing::IsEnabled; #else return NanotraceHR::Tracing::IsDisabled; #endif @@ -31,7 +31,7 @@ namespace ModelTracing { constexpr NanotraceHR::Tracing tracingStatus() { #ifdef ENABLE_MODEL_TRACING - return NanotraceHR::tracingStatus(); + return NanotraceHR::Tracing::IsEnabled; #else return NanotraceHR::Tracing::IsDisabled; #endif From a430ffc6cbd656f4d2186b853393ffb00315f048 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 14 Feb 2024 16:15:17 +0100 Subject: [PATCH 050/176] QmLDesigner: Improve node and property tracking Task-number: QDS-11952 Change-Id: I83afdc892e0c196e5e5abe56a4e9c32feb5ad1bf Reviewed-by: Tim Jenssen --- .../designercore/model/internalnode_p.h | 10 +++- .../designercore/model/internalproperty.cpp | 46 +++++++++++++++---- .../designercore/model/internalproperty.h | 2 +- .../qmldesigner/designercore/model/model.cpp | 3 +- .../designercore/tracing/qmldesignertracing.h | 1 + 5 files changed, 48 insertions(+), 14 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/model/internalnode_p.h b/src/plugins/qmldesigner/designercore/model/internalnode_p.h index 7c0bbb9bb36..66dbf02a333 100644 --- a/src/plugins/qmldesigner/designercore/model/internalnode_p.h +++ b/src/plugins/qmldesigner/designercore/model/internalnode_p.h @@ -39,6 +39,10 @@ class InternalNode; using InternalNodePointer = std::shared_ptr; using InternalPropertyPointer = std::shared_ptr; +using NanotraceHR::dictonary; +using NanotraceHR::keyValue; +using namespace std::literals::string_view_literals; + class InternalNode : public std::enable_shared_from_this { friend InternalProperty; @@ -53,7 +57,9 @@ public: , minorVersion(minorVersion) , isValid(true) , internalId(internalId) - , traceToken(ModelTracing::category().beginObject("InternalNode"_t)) + , traceToken(ModelTracing::category().beginAsynchronous("InternalNode"_t, + keyValue("type", typeName), + keyValue("internal id", internalId))) {} InternalNodeAbstractProperty::Pointer parentProperty() const { return m_parentProperty.lock(); } @@ -224,7 +230,7 @@ public: ModuleId moduleId; ImportedTypeNameId importedTypeNameId; TypeId typeId; - NO_UNIQUE_ADDRESS ModelTracing::ObjectTraceToken traceToken; + NO_UNIQUE_ADDRESS ModelTracing::AsynchronousToken traceToken; private: AuxiliaryDatas m_auxiliaryDatas; diff --git a/src/plugins/qmldesigner/designercore/model/internalproperty.cpp b/src/plugins/qmldesigner/designercore/model/internalproperty.cpp index f2096290416..9d83c31e0fc 100644 --- a/src/plugins/qmldesigner/designercore/model/internalproperty.cpp +++ b/src/plugins/qmldesigner/designercore/model/internalproperty.cpp @@ -4,17 +4,40 @@ #include "internalproperty.h" #include "internalnode_p.h" -namespace QmlDesigner::Internal { +namespace QmlDesigner { -static auto traceTokenInitArg(const QByteArray &name) +template void convertToString(String &string, PropertyType type) { - return ModelTracing::category().beginObject("InternalProperty"_t, keyValue("name", name)); + string.append("\""); + + switch (type) { + case PropertyType::None: + string.append("None"_sv); + break; + case PropertyType::Variant: + string.append("Variant"_sv); + break; + case PropertyType::Node: + string.append("Node"_sv); + break; + case PropertyType::NodeList: + string.append("NodeList"_sv); + break; + case PropertyType::Binding: + string.append("Binding"_sv); + break; + case PropertyType::SignalHandler: + string.append("SignalHandler"_sv); + break; + case PropertyType::SignalDeclaration: + string.append("SignalDeclaration"_sv); + break; + } + + string.append("\""); } -// Creates invalid InternalProperty -InternalProperty::InternalProperty() - : traceToken(traceTokenInitArg(m_name)) -{} +namespace Internal { InternalProperty::~InternalProperty() = default; @@ -24,7 +47,10 @@ InternalProperty::InternalProperty(const PropertyName &name, : m_name(name) , m_propertyOwner(propertyOwner) , m_propertyType(propertyType) - , traceToken(traceTokenInitArg(m_name)) + , traceToken(propertyOwner->traceToken.begin("InternalProperty"_t, + keyValue("name", name), + keyValue("owner", propertyOwner->internalId), + keyValue("type", propertyType))) {} bool InternalProperty::isValid() const @@ -52,5 +78,5 @@ void InternalProperty::resetDynamicTypeName() m_dynamicType.clear(); } -} //namespace QmlDesigner::Internal - +} // namespace Internal +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/model/internalproperty.h b/src/plugins/qmldesigner/designercore/model/internalproperty.h index 2fc77b2027f..2313929f5c1 100644 --- a/src/plugins/qmldesigner/designercore/model/internalproperty.h +++ b/src/plugins/qmldesigner/designercore/model/internalproperty.h @@ -193,7 +193,7 @@ private: TypeName m_dynamicType; std::weak_ptr m_propertyOwner; PropertyType m_propertyType = PropertyType::None; - NO_UNIQUE_ADDRESS ModelTracing::ObjectTraceToken traceToken; + NO_UNIQUE_ADDRESS ModelTracing::AsynchronousToken traceToken; }; } // namespace Internal diff --git a/src/plugins/qmldesigner/designercore/model/model.cpp b/src/plugins/qmldesigner/designercore/model/model.cpp index 67001855757..ec6d61dc26c 100644 --- a/src/plugins/qmldesigner/designercore/model/model.cpp +++ b/src/plugins/qmldesigner/designercore/model/model.cpp @@ -338,6 +338,7 @@ void ModelPrivate::removeNodeFromModel(const InternalNodePointer &node) if (!node->id.isEmpty()) m_idNodeHash.remove(node->id); node->isValid = false; + node->traceToken.end(); m_nodes.removeOne(node); m_internalIdNodeHash.remove(node->internalId); } @@ -480,7 +481,7 @@ void ModelPrivate::changeNodeId(const InternalNodePointer &node, const QString & const QString oldId = node->id; node->id = id; - node->traceToken.change("id"_t, std::forward_as_tuple("id", id)); + node->traceToken.tick("id"_t, std::forward_as_tuple("id", id)); if (!oldId.isEmpty()) m_idNodeHash.remove(oldId); if (!id.isEmpty()) diff --git a/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.h b/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.h index 704497f1b7e..6d13d32b30b 100644 --- a/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.h +++ b/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.h @@ -39,6 +39,7 @@ constexpr NanotraceHR::Tracing tracingStatus() using Category = NanotraceHR::StringViewWithStringArgumentsCategory; using ObjectTraceToken = Category::ObjectTokenType; +using AsynchronousToken = Category::AsynchronousTokenType; QMLDESIGNERCORE_EXPORT Category &category(); } // namespace ModelTracing From 4b0ea7b8fd506692f5c529022b2459ed4cbd6bf9 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 14 Feb 2024 16:16:03 +0100 Subject: [PATCH 051/176] QmlDesigner: Make model nodes invalid on model deletion Change-Id: I37fabba902ed2a6cd0940abeb63606e335bb76ab Reviewed-by: Tim Jenssen Reviewed-by: Qt CI Patch Build Bot --- .../designercore/instances/nodeinstanceview.cpp | 1 + src/plugins/qmldesigner/designercore/model/model.cpp | 1 + src/plugins/qmldesigner/designercore/model/model_p.h | 2 +- tests/unit/tests/unittests/model/model-test.cpp | 10 +++++----- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp index f16acf380f8..8988b7d09c9 100644 --- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp +++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp @@ -848,6 +848,7 @@ void NodeInstanceView::removeInstanceNodeRelationship(const ModelNode &node) Q_ASSERT(m_nodeInstanceHash.contains(node)); NodeInstance instance = instanceForModelNode(node); m_nodeInstanceHash.remove(node); + m_statePreviewImage.remove(node); instance.makeInvalid(); } diff --git a/src/plugins/qmldesigner/designercore/model/model.cpp b/src/plugins/qmldesigner/designercore/model/model.cpp index ec6d61dc26c..c9fca30b23f 100644 --- a/src/plugins/qmldesigner/designercore/model/model.cpp +++ b/src/plugins/qmldesigner/designercore/model/model.cpp @@ -136,6 +136,7 @@ ModelPrivate::ModelPrivate(Model *model, ModelPrivate::~ModelPrivate() { + removeNode(rootNode()); if constexpr (useProjectStorage()) projectStorage->removeObserver(this); }; diff --git a/src/plugins/qmldesigner/designercore/model/model_p.h b/src/plugins/qmldesigner/designercore/model/model_p.h index d7743b028e2..0af3cf3b7ca 100644 --- a/src/plugins/qmldesigner/designercore/model/model_p.h +++ b/src/plugins/qmldesigner/designercore/model/model_p.h @@ -133,7 +133,6 @@ public: /*factory methods for internal use in model and rewriter*/ void removeNodeAndRelatedResources(const InternalNodePointer &node); - void removeNode(const InternalNodePointer &node); void changeNodeId(const InternalNodePointer &node, const QString &id); void changeNodeType(const InternalNodePointer &node, const TypeName &typeName, int majorVersion, int minorVersion); @@ -310,6 +309,7 @@ public: protected: void removedTypeIds(const TypeIds &removedTypeIds) override; + void removeNode(const InternalNodePointer &node); private: void removePropertyWithoutNotification(InternalProperty *property); diff --git a/tests/unit/tests/unittests/model/model-test.cpp b/tests/unit/tests/unittests/model/model-test.cpp index 949efb3ee0a..bda942fec0b 100644 --- a/tests/unit/tests/unittests/model/model-test.cpp +++ b/tests/unit/tests/unittests/model/model-test.cpp @@ -89,7 +89,7 @@ protected: auto createNodeWithParent(const ModelNode &parentNode, const QString &id = {}) { - auto node = viewMock.createModelNode("QtQuick.Item"); + auto node = parentNode.view()->createModelNode("QtQuick.Item"); node.setIdWithoutRefactoring(id); parentNode.defaultNodeAbstractProperty().reparentHere(node); @@ -113,7 +113,6 @@ protected: } protected: - NiceMock viewMock; NiceMock pathCacheMock{"/path/foo.qml"}; NiceMock projectStorageMock{pathCacheMock.sourceId}; NiceMock resourceManagementMock; @@ -124,6 +123,7 @@ protected: nullptr, std::make_unique( resourceManagementMock)}; + NiceMock viewMock; QmlDesigner::SourceId filePathId = pathCacheMock.sourceId; QmlDesigner::TypeId itemTypeId = projectStorageMock.typeId(projectStorageMock.moduleId( "QtQuick"), @@ -515,8 +515,8 @@ TEST_F(Model, TEST_F(Model, by_default_remove_model_node_removes_node) { - model.detachView(&viewMock); QmlDesigner::Model newModel{{projectStorageMock, pathCacheMock}, "QtQuick.Item"}; + NiceMock viewMock; newModel.attachView(&viewMock); auto node = createNodeWithParent(viewMock.rootModelNode()); @@ -527,8 +527,8 @@ TEST_F(Model, by_default_remove_model_node_removes_node) TEST_F(Model, by_default_remove_properties_removes_property) { - model.detachView(&viewMock); QmlDesigner::Model newModel{{projectStorageMock, pathCacheMock}, "QtQuick.Item"}; + NiceMock viewMock; newModel.attachView(&viewMock); rootNode = viewMock.rootModelNode(); auto property = createProperty(rootNode, "yi"); @@ -644,8 +644,8 @@ TEST_F(Model, remove_model_nodes_bypasses_model_resource_management) TEST_F(Model, by_default_remove_model_nodes_in_factory_method_calls_removes_node) { - model.detachView(&viewMock); QmlDesigner::Model newModel{{projectStorageMock, pathCacheMock}, "QtQuick.Item"}; + NiceMock viewMock; newModel.attachView(&viewMock); rootNode = viewMock.rootModelNode(); auto node = createNodeWithParent(rootNode, "yi"); From 1b64158c7ab6ae4028e63bbd77756bc732146adb Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 15 Feb 2024 08:21:07 +0100 Subject: [PATCH 052/176] Nanotrace: Make category uncopyable Leads to subtle bugs. Task-number: QDS-11952 Change-Id: I022af297e89a08d9ed98cee6b76c6e2114043987 Reviewed-by: Tim Jenssen --- src/libs/nanotrace/nanotracehr.h | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/libs/nanotrace/nanotracehr.h b/src/libs/nanotrace/nanotracehr.h index 3241d3ded81..5f3daa5496f 100644 --- a/src/libs/nanotrace/nanotracehr.h +++ b/src/libs/nanotrace/nanotracehr.h @@ -657,7 +657,7 @@ public: std::size_t bindId = 0; if (m_id) { - auto category = m_category(); + auto &category = m_category(); bindId = category.createBindId(); category.begin('b', m_id, name, bindId, IsFlow::Out, std::forward(arguments)...); } @@ -954,12 +954,12 @@ public: std::size_t bindId = 0; if (m_id) { - auto category = m_category(); + auto &category = m_category(); bindId = category.createBindId(); category.tick('n', m_id, name, bindId, IsFlow::Out, std::forward(arguments)...); } - return {std::move(name), bindId, m_category}; + return {PrivateTag{}, std::move(name), bindId, m_category}; } template @@ -1072,9 +1072,9 @@ public: std::size_t id = 0; if (m_bindId) { - auto category = m_category(); + auto &category = m_category(); id = category.createId(); - category->begin('b', id, name, m_bindId, IsFlow::In, std::forward(arguments)...); + category.begin('b', id, name, m_bindId, IsFlow::In, std::forward(arguments)...); } return {std::move(name), id, m_category}; @@ -1088,10 +1088,10 @@ public: std::size_t bindId = 0; if (m_bindId) { - auto category = m_category(); + auto &category = m_category(); id = category.createId(); bindId = category.createBindId(); - category->begin('b', id, name, bindId, IsFlow::InOut, std::forward(arguments)...); + category.begin('b', id, name, bindId, IsFlow::InOut, std::forward(arguments)...); } return {std::piecewise_construct, @@ -1232,6 +1232,9 @@ public: m_bindIdCounter = m_globalBindIdCounter += 1ULL << 32; } + Category(const Category &) = delete; + Category &operator=(const Category &) = delete; + template [[nodiscard]] AsynchronousTokenType beginAsynchronous(ArgumentType traceName, Arguments &&...arguments) From e1a2454e8314e5683de39a40cef64311f82ce7f6 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 15 Feb 2024 08:22:26 +0100 Subject: [PATCH 053/176] QmlDesigner: Tracing shows the node model relationship Task-number: QDS-11952 Change-Id: I02769f07f3eadbe7f3e4095203619924cd545579 Reviewed-by: Tim Jenssen --- .../qmldesigner/designercore/model/internalnode_p.h | 12 ++++++++---- src/plugins/qmldesigner/designercore/model/model.cpp | 6 +++++- src/plugins/qmldesigner/designercore/model/model_p.h | 6 ++++++ 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/model/internalnode_p.h b/src/plugins/qmldesigner/designercore/model/internalnode_p.h index 66dbf02a333..f3fe55f2315 100644 --- a/src/plugins/qmldesigner/designercore/model/internalnode_p.h +++ b/src/plugins/qmldesigner/designercore/model/internalnode_p.h @@ -51,15 +51,19 @@ public: using Pointer = std::shared_ptr; using WeakPointer = std::weak_ptr; - explicit InternalNode(TypeName typeName, int majorVersion, int minorVersion, qint32 internalId) + explicit InternalNode(TypeName typeName, + int majorVersion, + int minorVersion, + qint32 internalId, + ModelTracing::Category::FlowTokenType flowTraceToken) : typeName(std::move(typeName)) , majorVersion(majorVersion) , minorVersion(minorVersion) , isValid(true) , internalId(internalId) - , traceToken(ModelTracing::category().beginAsynchronous("InternalNode"_t, - keyValue("type", typeName), - keyValue("internal id", internalId))) + , traceToken(flowTraceToken.beginAsynchronous("InternalNode"_t, + keyValue("type", typeName), + keyValue("internal id", internalId))) {} InternalNodeAbstractProperty::Pointer parentProperty() const { return m_parentProperty.lock(); } diff --git a/src/plugins/qmldesigner/designercore/model/model.cpp b/src/plugins/qmldesigner/designercore/model/model.cpp index c9fca30b23f..611ea4253d0 100644 --- a/src/plugins/qmldesigner/designercore/model/model.cpp +++ b/src/plugins/qmldesigner/designercore/model/model.cpp @@ -295,7 +295,11 @@ InternalNodePointer ModelPrivate::createNode(const TypeName &typeName, if (!isRootNode) internalId = m_internalIdCounter++; - auto newNode = std::make_shared(typeName, majorVersion, minorVersion, internalId); + auto newNode = std::make_shared(typeName, + majorVersion, + minorVersion, + internalId, + m_traceToken.tickWithFlow("create node"_t)); setTypeId(newNode.get(), typeName); diff --git a/src/plugins/qmldesigner/designercore/model/model_p.h b/src/plugins/qmldesigner/designercore/model/model_p.h index 0af3cf3b7ca..07aff386bb6 100644 --- a/src/plugins/qmldesigner/designercore/model/model_p.h +++ b/src/plugins/qmldesigner/designercore/model/model_p.h @@ -5,6 +5,8 @@ #include "qmldesignercorelib_global.h" +#include + #include "abstractview.h" #ifndef QDS_USE_PROJECTSTORAGE # include "metainfo.h" @@ -37,6 +39,8 @@ class NodeMetaInfoPrivate; namespace Internal { +using namespace NanotraceHR::Literals; + class InternalNode; class InternalProperty; class InternalBindingProperty; @@ -353,6 +357,8 @@ private: QPointer m_nodeInstanceView; QPointer m_metaInfoProxyModel; QHash> m_nodeMetaInfoCache; + ModelTracing::AsynchronousToken m_traceToken = ModelTracing::category().beginAsynchronous( + "Model"_t); bool m_writeLock = false; qint32 m_internalIdCounter = 1; }; From edd8d442e554fcbb96feac932c98d38659d6940e Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 15 Feb 2024 09:35:56 +0100 Subject: [PATCH 054/176] Nanotrace: Add QVariant string converter Task-number: QDS-11952 Change-Id: I874d29c0a7e21a61d3205d64a5c6137084ffcbab Reviewed-by: Tim Jenssen --- src/libs/nanotrace/nanotracehr.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/libs/nanotrace/nanotracehr.h b/src/libs/nanotrace/nanotracehr.h index 5f3daa5496f..6fe49cf3e11 100644 --- a/src/libs/nanotrace/nanotracehr.h +++ b/src/libs/nanotrace/nanotracehr.h @@ -10,6 +10,7 @@ #include #include +#include #include #include @@ -150,6 +151,18 @@ void convertToString(String &string, double number) string.append(Utils::SmallString::number(number)); } +template +void convertToString(String &string, const QString &text) +{ + convertToString(string, QStringView{text}); +} + +template +void convertToString(String &string, const QVariant &value) +{ + convertToString(string, value.toString()); +} + template void convertToString(String &string, const std::tuple &dictonary); From effaff53656d73177e0d7b6d077c154ae925b483 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 15 Feb 2024 09:36:16 +0100 Subject: [PATCH 055/176] Nanotrace: Add connect to a tick Task-number: QDS-11952 Change-Id: I07f075c7a556fc713ef56558a7a79a6bf68f2126 Reviewed-by: Tim Jenssen --- src/libs/nanotrace/nanotracehr.h | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/libs/nanotrace/nanotracehr.h b/src/libs/nanotrace/nanotracehr.h index 6fe49cf3e11..36b4b87c68f 100644 --- a/src/libs/nanotrace/nanotracehr.h +++ b/src/libs/nanotrace/nanotracehr.h @@ -855,6 +855,10 @@ public: void tick(ArgumentType, Arguments &&...) {} + template + void tick(const FlowTokenType &, ArgumentType, Arguments &&...) + {} + template FlowTokenType tickWithFlow(ArgumentType, Arguments &&...) { @@ -961,6 +965,19 @@ public: } } + template + void tick(const FlowTokenType &flowToken, ArgumentType name, Arguments &&...arguments) + { + if (m_id) { + m_category().tick('n', + m_id, + std::move(name), + flowToken.bindId(), + IsFlow::In, + std::forward(arguments)...); + } + } + template FlowTokenType tickWithFlow(ArgumentType name, Arguments &&...arguments) { @@ -1151,6 +1168,8 @@ public: m_category().tick('i', 0, name, m_bindId, IsFlow::In, std::forward(arguments)...); } + std::size_t bindId() const { return m_bindId; } + private: StringType m_name; std::size_t m_bindId = 0; From 6fa00b81290a18e6fd33f875538109130962c597 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 15 Feb 2024 09:36:44 +0100 Subject: [PATCH 056/176] QmlDesigner: Add more tracing to properties Task-number: QDS-11952 Change-Id: I27376acc1a77291de2253a295df0e3cecce1427e Reviewed-by: Tim Jenssen --- .../designercore/model/internalbindingproperty.cpp | 3 +++ .../designercore/model/internalnodelistproperty.cpp | 8 ++++++++ .../designercore/model/internalnodeproperty.cpp | 8 ++++++++ .../qmldesigner/designercore/model/internalproperty.cpp | 6 +++++- .../qmldesigner/designercore/model/internalproperty.h | 2 ++ .../designercore/model/internalsignalhandlerproperty.cpp | 4 ++++ .../designercore/model/internalvariantproperty.cpp | 2 ++ 7 files changed, 32 insertions(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/designercore/model/internalbindingproperty.cpp b/src/plugins/qmldesigner/designercore/model/internalbindingproperty.cpp index 6a3eee19753..2e7542c79b1 100644 --- a/src/plugins/qmldesigner/designercore/model/internalbindingproperty.cpp +++ b/src/plugins/qmldesigner/designercore/model/internalbindingproperty.cpp @@ -21,8 +21,11 @@ QString InternalBindingProperty::expression() const { return m_expression; } + void InternalBindingProperty::setExpression(const QString &expression) { + traceToken.tick("expression"_t, keyValue("expression", expression)); + m_expression = expression; } diff --git a/src/plugins/qmldesigner/designercore/model/internalnodelistproperty.cpp b/src/plugins/qmldesigner/designercore/model/internalnodelistproperty.cpp index c5aaa8fa94a..c0d3944b066 100644 --- a/src/plugins/qmldesigner/designercore/model/internalnodelistproperty.cpp +++ b/src/plugins/qmldesigner/designercore/model/internalnodelistproperty.cpp @@ -40,12 +40,20 @@ int InternalNodeListProperty::indexOf(const InternalNode::Pointer &node) const void InternalNodeListProperty::add(const InternalNode::Pointer &internalNode) { Q_ASSERT(!m_nodeList.contains(internalNode)); + + auto flowToken = traceToken.tickWithFlow("add node"_t); + internalNode->traceToken.tick(flowToken, "node added"_t); + m_nodeList.append(internalNode); } void InternalNodeListProperty::remove(const InternalNodePointer &internalNode) { Q_ASSERT(m_nodeList.contains(internalNode)); + + auto flowToken = traceToken.tickWithFlow("remove node"_t); + internalNode->traceToken.tick(flowToken, "node removed"_t); + m_nodeList.removeAll(internalNode); } diff --git a/src/plugins/qmldesigner/designercore/model/internalnodeproperty.cpp b/src/plugins/qmldesigner/designercore/model/internalnodeproperty.cpp index df9f5593725..ff9d71d6fdb 100644 --- a/src/plugins/qmldesigner/designercore/model/internalnodeproperty.cpp +++ b/src/plugins/qmldesigner/designercore/model/internalnodeproperty.cpp @@ -42,6 +42,10 @@ bool InternalNodeProperty::isValid() const void InternalNodeProperty::remove([[maybe_unused]] const InternalNode::Pointer &node) { Q_ASSERT(m_node == node); + + auto flowToken = traceToken.tickWithFlow("remove node"_t); + node->traceToken.tick(flowToken, "node removed"_t); + m_node.reset(); } @@ -49,6 +53,10 @@ void InternalNodeProperty::add(const InternalNode::Pointer &node) { Q_ASSERT(node); Q_ASSERT(node->parentProperty()); + + auto flowToken = traceToken.tickWithFlow("add node"_t); + node->traceToken.tick(flowToken, "node added"_t); + m_node = node; } diff --git a/src/plugins/qmldesigner/designercore/model/internalproperty.cpp b/src/plugins/qmldesigner/designercore/model/internalproperty.cpp index 9d83c31e0fc..0b5b56adf08 100644 --- a/src/plugins/qmldesigner/designercore/model/internalproperty.cpp +++ b/src/plugins/qmldesigner/designercore/model/internalproperty.cpp @@ -70,12 +70,16 @@ TypeName InternalProperty::dynamicTypeName() const void InternalProperty::setDynamicTypeName(const TypeName &name) { + traceToken.tick("dynamic type name"_t, keyValue("name", name)); + m_dynamicType = name; } void InternalProperty::resetDynamicTypeName() { - m_dynamicType.clear(); + traceToken.tick("reset dynamic type name"_t); + + m_dynamicType.clear(); } } // namespace Internal diff --git a/src/plugins/qmldesigner/designercore/model/internalproperty.h b/src/plugins/qmldesigner/designercore/model/internalproperty.h index 2313929f5c1..f5ca983969c 100644 --- a/src/plugins/qmldesigner/designercore/model/internalproperty.h +++ b/src/plugins/qmldesigner/designercore/model/internalproperty.h @@ -193,6 +193,8 @@ private: TypeName m_dynamicType; std::weak_ptr m_propertyOwner; PropertyType m_propertyType = PropertyType::None; + +protected: NO_UNIQUE_ADDRESS ModelTracing::AsynchronousToken traceToken; }; diff --git a/src/plugins/qmldesigner/designercore/model/internalsignalhandlerproperty.cpp b/src/plugins/qmldesigner/designercore/model/internalsignalhandlerproperty.cpp index 70aade63a68..d199ad4b8c9 100644 --- a/src/plugins/qmldesigner/designercore/model/internalsignalhandlerproperty.cpp +++ b/src/plugins/qmldesigner/designercore/model/internalsignalhandlerproperty.cpp @@ -23,6 +23,8 @@ QString InternalSignalHandlerProperty::source() const } void InternalSignalHandlerProperty::setSource(const QString &source) { + traceToken.tick("source"_t, keyValue("source", source)); + m_source = source; } @@ -38,6 +40,8 @@ QString InternalSignalDeclarationProperty::signature() const void InternalSignalDeclarationProperty::setSignature(const QString &signature) { + traceToken.tick("signature"_t, keyValue("signature", signature)); + m_signature = signature; } diff --git a/src/plugins/qmldesigner/designercore/model/internalvariantproperty.cpp b/src/plugins/qmldesigner/designercore/model/internalvariantproperty.cpp index 53a0347c173..04ff6fff708 100644 --- a/src/plugins/qmldesigner/designercore/model/internalvariantproperty.cpp +++ b/src/plugins/qmldesigner/designercore/model/internalvariantproperty.cpp @@ -19,6 +19,8 @@ QVariant InternalVariantProperty::value() const void InternalVariantProperty::setValue(const QVariant &value) { + traceToken.tick("value"_t, keyValue("value", value)); + m_value = value; } From 12ad750d16bf261a06801408661523a5dd4778f7 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 15 Feb 2024 11:59:05 +0100 Subject: [PATCH 057/176] Nanotrace: Remove ObjectToken There is no direct support in Perfetto. It can be simulated by asynchronous tokens but then asynchronous tokens can be used directly. Task-number: QDS-11952 Change-Id: I62dff6e8c1e93b71ccdc7b1678817d1909afa37c Reviewed-by: Tim Jenssen --- src/libs/nanotrace/nanotracehr.h | 114 ------------------ .../designercore/tracing/qmldesignertracing.h | 1 - 2 files changed, 115 deletions(-) diff --git a/src/libs/nanotrace/nanotracehr.h b/src/libs/nanotrace/nanotracehr.h index 36b4b87c68f..1a244bd5f44 100644 --- a/src/libs/nanotrace/nanotracehr.h +++ b/src/libs/nanotrace/nanotracehr.h @@ -724,101 +724,6 @@ using StringViewWithStringArgumentsCategory = Category, Tracing::IsDisabled>; -template -class ObjectToken : public BasicDisabledToken -{ -public: - using ArgumentType = typename Category::ArgumentType; - - ObjectToken() = default; - ObjectToken(const ObjectToken &) = delete; - ObjectToken &operator=(const ObjectToken &) = delete; - ObjectToken(ObjectToken &&other) noexcept = default; - ObjectToken &operator=(ObjectToken &&other) noexcept = default; - ~ObjectToken() = default; - - template - void change(ArgumentType, Arguments &&...) - {} -}; - -template -class ObjectToken : public BasicEnabledToken -{ - using CategoryFunctionPointer = typename Category::CategoryFunctionPointer; - - ObjectToken(std::string_view name, std::size_t id, CategoryFunctionPointer category) - : m_name{name} - , m_id{id} - , m_category{category} - {} - -public: - using StringType = typename Category::StringType; - using ArgumentType = typename Category::ArgumentType; - - friend Category; - - ObjectToken(const ObjectToken &other) - : m_name{other.m_name} - , m_category{other.m_category} - { - if (other.m_id) - m_id = m_category().beginObject(m_name).m_id; - } - - ObjectToken &operator=(const ObjectToken &other) - { - if (this != &other) { - ~ObjectToken(); - if (other.m_id) { - m_category = other.m_category; - m_name = other.m_name; - m_id = other.m_category->beginObject(other.m_name).m_id; - } - } - } - - ObjectToken(ObjectToken &&other) noexcept - : m_name{std::move(other.m_name)} - , m_id{std::exchange(other.m_id, 0)} - , m_category{std::exchange(other.m_category, nullptr)} - {} - - ObjectToken &operator=(ObjectToken &&other) noexcept - { - if (&other != this) { - m_name = std::move(other.m_name); - m_id = std::exchange(other.m_id, 0); - m_category = std::exchange(other.m_category, nullptr); - } - - return *this; - } - - ~ObjectToken() - { - if (m_id) - m_category().end('e', m_id, std::move(m_name)); - - m_id = 0; - } - - template - void change(ArgumentType name, Arguments &&...arguments) - { - if (m_id) { - m_category().tick( - 'n', m_id, std::move(name), 0, IsFlow::No, std::forward(arguments)...); - } - } - -private: - StringType m_name; - std::size_t m_id = 0; - CategoryFunctionPointer m_category = nullptr; -}; - template class AsynchronousToken : public BasicDisabledToken { @@ -1184,7 +1089,6 @@ public: using ArgumentType = typename TraceEvent::ArgumentType; using ArgumentsStringType = typename TraceEvent::ArgumentsStringType; using AsynchronousTokenType = AsynchronousToken; - using ObjectTokenType = ObjectToken; using FlowTokenType = FlowToken; using TracerType = Tracer; using TokenType = Token; @@ -1206,12 +1110,6 @@ public: ArgumentType, Arguments &&...) {} - template - [[nodiscard]] ObjectTokenType beginObject(ArgumentType, Arguments &&...) - { - return {}; - } - template [[nodiscard]] TracerType beginDuration(ArgumentType, Arguments &&...) { @@ -1240,14 +1138,12 @@ public: using ArgumentsStringType = typename TraceEvent::ArgumentsStringType; using StringType = typename TraceEvent::StringType; using AsynchronousTokenType = AsynchronousToken; - using ObjectTokenType = ObjectToken; using FlowTokenType = FlowToken; using TracerType = Tracer; using TokenType = Token; using CategoryFunctionPointer = Category &(*) (); friend AsynchronousTokenType; - friend ObjectTokenType; friend TokenType; friend FlowTokenType; friend TracerType; @@ -1292,16 +1188,6 @@ public: std::forward_as_tuple(PrivateTag{}, traceName, bindId, m_self)}; } - template - [[nodiscard]] ObjectTokenType beginObject(ArgumentType traceName, Arguments &&...arguments) - { - std::size_t id = createId(); - - begin('b', id, std::move(traceName), 0, IsFlow::No, std::forward(arguments)...); - - return {traceName, id, m_self}; - } - template [[nodiscard]] TracerType beginDuration(ArgumentType traceName, Arguments &&...arguments) { diff --git a/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.h b/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.h index 6d13d32b30b..49691efc0b1 100644 --- a/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.h +++ b/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.h @@ -38,7 +38,6 @@ constexpr NanotraceHR::Tracing tracingStatus() } using Category = NanotraceHR::StringViewWithStringArgumentsCategory; -using ObjectTraceToken = Category::ObjectTokenType; using AsynchronousToken = Category::AsynchronousTokenType; QMLDESIGNERCORE_EXPORT Category &category(); From 788d4c004076297fa49fffab4b439f5919631600 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Thu, 15 Feb 2024 17:27:05 +0200 Subject: [PATCH 058/176] EffectComposer: Improve reordering nodes via drag The dragged effect is collapsed when dragging start, which helps with large nodes. Autoscrolling content was added when dragging nodes to the top/bottom of the scrollview. Fixes: QDS-10857 Change-Id: I5027324918a200746356704e36a6d99cb98aa04e Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri --- .../EffectComposer.qml | 134 ++++++++++++------ .../imports/HelperWidgets/Section.qml | 5 + 2 files changed, 95 insertions(+), 44 deletions(-) diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposer.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposer.qml index 7b4d8d04cf1..6e72a258cbb 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposer.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposer.qml @@ -191,17 +191,12 @@ ColumnLayout { HelperWidgets.ScrollView { id: scrollView + readonly property int dragScrollMargin: 50 + anchors.fill: parent clip: true interactive: !HelperWidgets.Controller.contextMenuOpened - onContentHeightChanged: { - if (scrollView.contentItem.height > scrollView.height) { - let lastItemH = repeater.itemAt(repeater.count - 1).height - scrollView.contentY = scrollView.contentItem.height - lastItemH - } - } - Column { id: nodesCol width: scrollView.width @@ -221,7 +216,10 @@ ColumnLayout { width: parent.width modelIndex: index + property bool wasExpanded: false + Behavior on y { + id: dragAnimation PropertyAnimation { duration: 300 easing.type: Easing.InOutQuad @@ -231,54 +229,29 @@ ColumnLayout { onStartDrag: (section) => { root.draggedSec = section root.moveFromIdx = index - + // We only need to animate non-dragged sections + dragAnimation.enabled = false + wasExpanded = expanded + expanded = false highlightBorder = true - root.secsY = [] - for (let i = 0; i < repeater.count; ++i) - root.secsY[i] = repeater.itemAt(i).y } onStopDrag: { - if (root.moveFromIdx === root.moveToIdx) - root.draggedSec.y = root.secsY[root.moveFromIdx] - else - root.backendModel.moveNode(root.moveFromIdx, root.moveToIdx) + if (root.secsY.length !== 0) { + if (root.moveFromIdx === root.moveToIdx) + root.draggedSec.y = root.secsY[root.moveFromIdx] + else + root.backendModel.moveNode(root.moveFromIdx, root.moveToIdx) + } highlightBorder = false root.draggedSec = null + expanded = wasExpanded + dragAnimation.enabled = true } } } // Repeater - - Timer { - running: root.draggedSec - interval: 50 - repeat: true - - onTriggered: { - root.moveToIdx = root.moveFromIdx - for (let i = 0; i < repeater.count; ++i) { - let currItem = repeater.itemAt(i) - if (i > root.moveFromIdx) { - if (root.draggedSec.y > currItem.y + (currItem.height - root.draggedSec.height) * .5) { - currItem.y = root.secsY[i] - root.draggedSec.height - nodesCol.spacing - root.moveToIdx = i - } else { - currItem.y = root.secsY[i] - } - } else if (i < root.moveFromIdx) { - if (!repeater.model.isDependencyNode(i) - && root.draggedSec.y < currItem.y + (currItem.height - root.draggedSec.height) * .5) { - currItem.y = root.secsY[i] + root.draggedSec.height + nodesCol.spacing - root.moveToIdx = Math.min(root.moveToIdx, i) - } else { - currItem.y = root.secsY[i] - } - } - } - } - } // Timer } // Column } // ScrollView @@ -295,4 +268,77 @@ ColumnLayout { } // Item } // Column } // SplitView + + function handleDragMove() { + dragTimer.stop() + if (root.secsY.length === 0) { + for (let i = 0; i < repeater.count; ++i) + root.secsY[i] = repeater.itemAt(i).y + } + + let oldContentY = scrollView.contentY + if (root.draggedSec.y < scrollView.dragScrollMargin + scrollView.contentY + && scrollView.contentY > 0) { + scrollView.contentY -= scrollView.dragScrollMargin / 2 + } else if (root.draggedSec.y > scrollView.contentY + scrollView.height - scrollView.dragScrollMargin + && scrollView.contentY < scrollView.contentHeight - scrollView.height) { + scrollView.contentY += scrollView.dragScrollMargin / 2 + if (scrollView.contentY > scrollView.contentHeight - scrollView.height) + scrollView.contentY = scrollView.contentHeight - scrollView.height + } + if (scrollView.contentY < 0) + scrollView.contentY = 0 + + if (oldContentY !== scrollView.contentY) { + // Changing dragged section position in drag handler doesn't seem to stick + // when triggered by mouse move, so do it again async + dragTimer.targetY = root.draggedSec.y - oldContentY + scrollView.contentY + dragTimer.restart() + dragConnection.enabled = false + root.draggedSec.y = dragTimer.targetY + dragConnection.enabled = true + } + + root.moveToIdx = root.moveFromIdx + for (let i = 0; i < repeater.count; ++i) { + let currItem = repeater.itemAt(i) + if (i > root.moveFromIdx) { + if (root.draggedSec.y > currItem.y) { + currItem.y = root.secsY[i] - root.draggedSec.height - nodesCol.spacing + root.moveToIdx = i + } else { + currItem.y = root.secsY[i] + } + } else if (i < root.moveFromIdx) { + if (!repeater.model.isDependencyNode(i) && root.draggedSec.y < currItem.y) { + currItem.y = root.secsY[i] + root.draggedSec.height + nodesCol.spacing + root.moveToIdx = Math.min(root.moveToIdx, i) + } else { + currItem.y = root.secsY[i] + } + } + } + } + + Connections { + id: dragConnection + target: root.draggedSec + onYChanged: root.handleDragMove() + } + + Timer { + id: dragTimer + running: false + interval: 16 + repeat: false + + property real targetY: -1 + + onTriggered: { + // Ensure we get position change triggers even if user holds mouse still to + // make scrolling smooth + root.draggedSec.y = targetY + root.handleDragMove() + } + } // Timer } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/Section.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/Section.qml index f078f3dd48d..dbbb200f738 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/Section.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/Section.qml @@ -318,5 +318,10 @@ Item { duration: 120 easing.type: Easing.OutCubic } + + onRunningChanged: { + if (!running) + enabled = false + } } } From d83a46461587c9bc0913e04a903cc6f94d983387 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Fri, 16 Feb 2024 14:57:41 +0200 Subject: [PATCH 059/176] EffectComposer: Fix scroll to end when adding a node Change-Id: Id13a825cedfe98500337d1914bda064c2f5e3bb3 Reviewed-by: Miikka Heikkinen --- .../effectComposerQmlSources/EffectComposer.qml | 10 ++++++++++ .../effectComposerQmlSources/EffectNodesComboBox.qml | 3 +++ 2 files changed, 13 insertions(+) diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposer.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposer.qml index 6e72a258cbb..fabed985893 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposer.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposer.qml @@ -145,6 +145,8 @@ ColumnLayout { color: StudioTheme.Values.themeToolbarBackground EffectNodesComboBox { + id: nodesComboBox + mainRoot: root anchors.verticalCenter: parent.verticalCenter @@ -197,6 +199,14 @@ ColumnLayout { clip: true interactive: !HelperWidgets.Controller.contextMenuOpened + onContentHeightChanged: { + if (nodesComboBox.nodeJustAdded && scrollView.contentItem.height > scrollView.height) { + let lastItemH = repeater.itemAt(repeater.count - 1).height + scrollView.contentY = scrollView.contentItem.height - lastItemH + nodesComboBox.nodeJustAdded = false + } + } + Column { id: nodesCol width: scrollView.width diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectNodesComboBox.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectNodesComboBox.qml index dbab36051d2..7051a9292fc 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectNodesComboBox.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectNodesComboBox.qml @@ -22,6 +22,8 @@ StudioControls.ComboBox { readonly property int popupHeight: Math.min(800, row.height + 2) + property bool nodeJustAdded: false + function calculateWindowGeometry() { var globalPos = EffectComposerBackend.rootView.globalPos(mainRoot.mapFromItem(root, 0, 0)) var screenRect = EffectComposerBackend.rootView.screenRect(); @@ -117,6 +119,7 @@ StudioControls.ComboBox { onAddEffectNode: (nodeQenPath) => { EffectComposerBackend.rootView.addEffectNode(modelData.nodeQenPath) root.popup.close() + root.nodeJustAdded = true } } } From 9d6d5216433b4eff132a5ca4430a0dc3b788eb72 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 16 Feb 2024 14:43:10 +0200 Subject: [PATCH 060/176] QmlDesigner: Fix crash when reparenting items into effected item Some effect related objects such as QQuickShaderEffectSource can be silently removed alongside another object, so use QPointer to ensure all objects are still valid before readding them. Fixes: QDS-11973 Change-Id: I75ee2dc81e27774f8be87b031e149b135ec84023 Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Patch Build Bot --- .../qml2puppet/instances/objectnodeinstance.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/tools/qml2puppet/qml2puppet/instances/objectnodeinstance.cpp b/src/tools/qml2puppet/qml2puppet/instances/objectnodeinstance.cpp index 941a1d50b90..a1c727f215b 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/objectnodeinstance.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/objectnodeinstance.cpp @@ -291,7 +291,7 @@ static void removeObjectFromList(const QQmlProperty &property, int count = listReference.count(); - QObjectList objectList; + QList> objectList; for (int i = 0; i < count; i ++) { QObject *listItem = listReference.at(i); @@ -301,8 +301,10 @@ static void removeObjectFromList(const QQmlProperty &property, listReference.clear(); - for (QObject *object : std::as_const(objectList)) - listReference.append(object); + for (QObject *object : std::as_const(objectList)) { + if (object) + listReference.append(object); + } } void ObjectNodeInstance::removeFromOldProperty(QObject *object, QObject *oldParent, const PropertyName &oldParentProperty) From c9d9b4072f5d95d7974d083e2de0a23b3b028cc9 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 16 Feb 2024 16:29:04 +0200 Subject: [PATCH 061/176] QmlDesigner: Disable eyeball in navigator for effect nodes Fixes: QDS-11918 Change-Id: Id47b396d7a54af64d21d8898fbfd5c82626ccb03 Reviewed-by: Mahmoud Badri Reviewed-by: Qt CI Patch Build Bot --- .../qmldesigner/components/navigator/navigatortreemodel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp index 3bfe26c74da..7cf02c72592 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp +++ b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp @@ -933,7 +933,7 @@ bool NavigatorTreeModel::setData(const QModelIndex &index, const QVariant &value if (index.column() == ColumnType::Alias && role == Qt::CheckStateRole) { m_view->handleChangedExport(modelNode, value.toInt() != 0); } else if (index.column() == ColumnType::Visibility && role == Qt::CheckStateRole) { - if (m_view->isPartOfMaterialLibrary(modelNode)) + if (m_view->isPartOfMaterialLibrary(modelNode) || QmlItemNode(modelNode).isEffectItem()) return false; QmlVisualNode(modelNode).setVisibilityOverride(value.toInt() == 0); } else if (index.column() == ColumnType::Lock && role == Qt::CheckStateRole) { From fe08adbf65d274e5752b1e0f5e5d790026957c1a Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Mon, 19 Feb 2024 17:05:59 +0100 Subject: [PATCH 062/176] qds: increase to version 4.5 Change-Id: I119eef191fda8a358457a35b2f45b1dd7565b78b Reviewed-by: Tim Jenssen --- dist/branding/qtdesignstudio/QtCreatorIDEBranding.cmake | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dist/branding/qtdesignstudio/QtCreatorIDEBranding.cmake b/dist/branding/qtdesignstudio/QtCreatorIDEBranding.cmake index 59b9fdf3138..e2131bcc757 100644 --- a/dist/branding/qtdesignstudio/QtCreatorIDEBranding.cmake +++ b/dist/branding/qtdesignstudio/QtCreatorIDEBranding.cmake @@ -1,7 +1,7 @@ -set(IDE_VERSION "4.4.0") # The IDE version. -set(IDE_VERSION_COMPAT "4.4.0") # The IDE Compatibility version. -set(IDE_VERSION_DISPLAY "4.4.0") # The IDE display version. -set(IDE_COPYRIGHT_YEAR "2023") # The IDE current copyright year. +set(IDE_VERSION "4.5.0") # The IDE version. +set(IDE_VERSION_COMPAT "4.5.0") # The IDE Compatibility version. +set(IDE_VERSION_DISPLAY "4.5.0") # The IDE display version. +set(IDE_COPYRIGHT_YEAR "2024") # The IDE current copyright year. set(IDE_SETTINGSVARIANT "QtProject") # The IDE settings variation. set(IDE_COPY_SETTINGSVARIANT "Nokia") # The IDE settings to initially import. From 695548cc812c7892607dc43bab6869d78668aa62 Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Mon, 19 Feb 2024 18:31:47 +0100 Subject: [PATCH 063/176] fix CrashPad restart dialog call Change-Id: I8764e78158d8ffa255ef26324a2f7359707865d1 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen --- src/plugins/coreplugin/systemsettings.cpp | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/plugins/coreplugin/systemsettings.cpp b/src/plugins/coreplugin/systemsettings.cpp index ccaef59b8b0..1937d125642 100644 --- a/src/plugins/coreplugin/systemsettings.cpp +++ b/src/plugins/coreplugin/systemsettings.cpp @@ -276,13 +276,7 @@ public: connect(helpCrashReportingButton, &QAbstractButton::clicked, this, [this] { showHelpDialog(Tr::tr("Crash Reporting"), CorePlugin::msgCrashpadInformation()); }); - connect(&s.enableCrashReporting, &BaseAspect::changed, this, [this] { - const QString restartText = Tr::tr("The change will take effect after restart."); - Core::RestartDialog restartDialog(Core::ICore::dialogParent(), restartText); - restartDialog.exec(); - if (restartDialog.result() == QDialog::Accepted) - apply(); - }); + connect(&s.enableCrashReporting, &BaseAspect::changed, this, &SystemSettingsWidget::apply); updateClearCrashWidgets(); connect(m_clearCrashReportsButton, &QPushButton::clicked, this, [&] { From fa14c8dfab7c34ab7a9c94af9e64b620cdaa2e23 Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Fri, 16 Feb 2024 18:41:13 +0100 Subject: [PATCH 064/176] QmlDesigner: Fix scrollTo multi select behavior Instead of scrolling to the first element in the selection, scroll to the last. This will keep the existing behavior for single selection and will fix the behavior for multi selection. Task-number: QDS-9794 Change-Id: I846c6ea6b967ef76a77d4ea0fc819d06772eca01 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Thomas Hartmann Reviewed-by: --- src/plugins/qmldesigner/components/navigator/navigatorview.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/components/navigator/navigatorview.cpp b/src/plugins/qmldesigner/components/navigator/navigatorview.cpp index 5cea0aab4ea..a3ad444e243 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatorview.cpp +++ b/src/plugins/qmldesigner/components/navigator/navigatorview.cpp @@ -700,7 +700,7 @@ void NavigatorView::updateItemSelection() blockSelectionChangedSignal(blocked); if (!selectedModelNodes().isEmpty()) - treeWidget()->scrollTo(indexForModelNode(selectedModelNodes().constFirst())); + treeWidget()->scrollTo(indexForModelNode(selectedModelNodes().constLast())); // make sure selected nodes are visible for (const QModelIndex &selectedIndex : itemSelection.indexes()) { From f926cbaaca75c7ef101b68d4521e52dcf86de96c Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Mon, 19 Feb 2024 17:49:13 +0200 Subject: [PATCH 065/176] EffectComposer: Preserve expand state of effect node sections Fixes: QDS-11974 Change-Id: I38d9efb8f05d5b0064fe9e85fdf4b624f5cef11f Reviewed-by: Mahmoud Badri --- .../EffectComposer.qml | 30 +++++++++++++++++++ .../effectcomposer/effectcomposerwidget.cpp | 5 ++++ 2 files changed, 35 insertions(+) diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposer.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposer.qml index fabed985893..af1c16491e0 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposer.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposer.qml @@ -22,6 +22,7 @@ ColumnLayout { property int moveFromIdx: 0 property int moveToIdx: 0 property bool previewAnimationRunning: false + property var expandStates: null // Invoked after save changes is done property var onSaveChangesCallback: () => {} @@ -33,6 +34,28 @@ ColumnLayout { saveChangesDialog.open() } + // Invoked from C++ side before resetting the model to store current expanded state of nodes + function storeExpandStates() { + expandStates = new Map() + + for (let i = 0; i < repeater.count; ++i) { + var curItem = repeater.itemAt(i) + expandStates.set(curItem.caption, curItem.expanded) + } + } + + // Invoked after model has been reset to restore expanded state for nodes + function restoreExpandStates() { + if (expandStates) { + for (let i = 0; i < repeater.count; ++i) { + var curItem = repeater.itemAt(i) + if (expandStates.has(curItem.caption)) + curItem.expanded = expandStates.get(curItem.caption) + } + expandStates = null + } + } + Connections { target: root.backendModel function onIsEmptyChanged() { @@ -200,6 +223,13 @@ ColumnLayout { interactive: !HelperWidgets.Controller.contextMenuOpened onContentHeightChanged: { + // Expand states are stored before full model reset. + // Content height change indicates the model has been updated after full + // reset, so we restore expand states if any are stored. + root.restoreExpandStates() + + // If content height change was because a recent node addition, we want to + // scroll to the end of the content so the newly added item is visible. if (nodesComboBox.nodeJustAdded && scrollView.contentItem.height > scrollView.height) { let lastItemH = repeater.itemAt(repeater.count - 1).height scrollView.contentY = scrollView.contentItem.height - lastItemH diff --git a/src/plugins/effectcomposer/effectcomposerwidget.cpp b/src/plugins/effectcomposer/effectcomposerwidget.cpp index 9826225fa46..3ae6e7b62f8 100644 --- a/src/plugins/effectcomposer/effectcomposerwidget.cpp +++ b/src/plugins/effectcomposer/effectcomposerwidget.cpp @@ -117,6 +117,11 @@ EffectComposerWidget::EffectComposerWidget(EffectComposerView *view) } }); + connect(m_effectComposerModel.data(), &EffectComposerModel::modelAboutToBeReset, + this, [this] { + QMetaObject::invokeMethod(quickWidget()->rootObject(), "storeExpandStates"); + }); + connect(Core::EditorManager::instance(), &Core::EditorManager::aboutToSave, this, [this] { if (m_effectComposerModel->hasUnsavedChanges()) { QString compName = m_effectComposerModel->currentComposition(); From 0f9782070ab33897815e1fcaff3718de86cd9e9d Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Tue, 20 Feb 2024 10:16:01 +0200 Subject: [PATCH 066/176] QmlDesigner: Create imports directory before copying template files Fixes: QDS-11946 Change-Id: I13f200c18d8b4775d6353ba994b83b8b72313e60 Reviewed-by: Miikka Heikkinen --- .../collectioneditor/collectioneditorutils.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp index d9bbc764156..7fc6eec379b 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp @@ -289,8 +289,13 @@ bool ensureDataStoreExists(bool &justCreated) return false; } - templatePath.copyFile(filePath); - if (filePath.exists()) { + if (!filePath.parentDir().ensureWritableDir()) { + qWarning() << Q_FUNC_INFO << __LINE__ << "Cannot create directory" + << filePath.parentDir(); + return false; + } + + if (templatePath.copyFile(filePath)) { justCreated = true; return true; } From bb63198f8ada23c9c1e0ba20555077d332185cc6 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Fri, 9 Feb 2024 14:14:19 +0200 Subject: [PATCH 067/176] QmlDesigner: Open collection editor for dropped ListView * A default color model will be created and assigned to the ListView when a ListView is dropped to the view. * The user can edit the assigned collection of the list model by having access to an action in the context menu of the form editor. Task-number: QDS-11671 Fixes: QDS-11792 Change-Id: I70252f6e34ccbc95d8b700459f45a11a76c81c50 Reviewed-by: Mahmoud Badri Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Miikka Heikkinen --- .../shared-plugin/name/Colors.json.tpl | 18 +++++ .../collectioneditorutils.cpp | 23 ++++++ .../collectioneditor/collectioneditorutils.h | 2 + .../collectioneditor/collectionlistmodel.cpp | 79 +++++++++++++------ .../collectioneditor/collectionlistmodel.h | 15 +++- .../collectionsourcemodel.cpp | 59 +++++++++++--- .../collectioneditor/collectionsourcemodel.h | 4 +- .../collectioneditor/collectionview.cpp | 58 +++++++++++++- .../collectioneditor/collectionview.h | 9 +++ .../collectioneditor/collectionwidget.cpp | 13 ++- .../collectioneditor/collectionwidget.h | 2 +- .../componentcore/componentcore_constants.h | 4 + .../componentcore/designeractionmanager.cpp | 28 +++++-- .../modelnodecontextmenu_helper.h | 12 +++ .../componentcore/modelnodeoperations.cpp | 24 ++++++ .../componentcore/modelnodeoperations.h | 1 + .../components/formeditor/dragtool.cpp | 7 +- .../navigator/navigatortreemodel.cpp | 4 +- .../designercore/include/nodemetainfo.h | 1 + .../designercore/metainfo/nodemetainfo.cpp | 10 +++ 20 files changed, 318 insertions(+), 55 deletions(-) create mode 100644 share/qtcreator/qmldesigner/studio_templates/projects/shared-plugin/name/Colors.json.tpl diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/shared-plugin/name/Colors.json.tpl b/share/qtcreator/qmldesigner/studio_templates/projects/shared-plugin/name/Colors.json.tpl new file mode 100644 index 00000000000..052b7abd015 --- /dev/null +++ b/share/qtcreator/qmldesigner/studio_templates/projects/shared-plugin/name/Colors.json.tpl @@ -0,0 +1,18 @@ +[ + { + "colorCode": "#ff0000", + "name": "Red" + }, + { + "colorCode": "#00ff00", + "name": "Green" + }, + { + "colorCode": "#0000ff", + "name": "Blue" + }, + { + "colorCode": "#ffffff", + "name": "White" + } +] diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp index 7fc6eec379b..fc925126350 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp @@ -381,6 +381,29 @@ QJsonObject defaultCollection() return collectionObject; } +QJsonObject defaultColorCollection() +{ + using Utils::FilePath; + using Utils::FileReader; + const FilePath templatePath = findFile(Core::ICore::resourcePath(), "Colors.json.tpl"); + + FileReader fileReader; + if (!fileReader.fetch(templatePath)) { + qWarning() << Q_FUNC_INFO << __LINE__ << "Can't read the content of the file" << templatePath; + return {}; + } + + QJsonParseError parseError; + const CollectionDetails collection = CollectionDetails::fromImportedJson(fileReader.data(), + &parseError); + if (parseError.error != QJsonParseError::NoError) { + qWarning() << Q_FUNC_INFO << __LINE__ << "Error in template file" << parseError.errorString(); + return {}; + } + + return collection.toLocalJson(); +} + QString dataTypeToString(CollectionDetails::DataType dataType) { static const QHash typeStringHash = CollectionDataTypeHelper::typeToStringHash(); diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h index 35a036c4e32..bd9e24d23c6 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h @@ -43,6 +43,8 @@ bool hasTextRoleProperty(const ModelNode &node); QJsonObject defaultCollection(); +QJsonObject defaultColorCollection(); + QString dataTypeToString(CollectionDetails::DataType dataType); CollectionDetails::DataType dataTypeFromString(const QString &dataType); diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.cpp index 214ece078a0..323f0e767fa 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.cpp @@ -3,9 +3,7 @@ #include "collectionlistmodel.h" -#include "collectioneditorconstants.h" #include "collectioneditorutils.h" -#include "variantproperty.h" #include #include @@ -27,7 +25,7 @@ bool containsItem(const std::initializer_list &container, const Value namespace QmlDesigner { CollectionListModel::CollectionListModel(const ModelNode &sourceModel) - : QStringListModel() + : QAbstractListModel() , m_sourceNode(sourceModel) , m_sourceType(CollectionEditorUtils::getSourceCollectionType(sourceModel)) { @@ -50,6 +48,11 @@ QHash CollectionListModel::roleNames() const return roles; } +int CollectionListModel::rowCount([[maybe_unused]] const QModelIndex &parent) const +{ + return m_data.count(); +} + bool CollectionListModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (!index.isValid()) @@ -60,9 +63,11 @@ bool CollectionListModel::setData(const QModelIndex &index, const QVariant &valu return false; QString oldName = collectionNameAt(index.row()); - bool nameChanged = Super::setData(index, value); + bool nameChanged = value != data(index); if (nameChanged) { - QString newName = collectionNameAt(index.row()); + QString newName = value.toString(); + m_data.replace(index.row(), newName); + emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole}); emit this->collectionNameChanged(oldName, newName); } return nameChanged; @@ -78,22 +83,26 @@ bool CollectionListModel::setData(const QModelIndex &index, const QVariant &valu bool CollectionListModel::removeRows(int row, int count, const QModelIndex &parent) { const int rows = rowCount(parent); - if (count < 1 || row >= rows) + if (row >= rows) return false; row = qBound(0, row, rows - 1); - count = qBound(1, count, rows - row); + count = qBound(0, count, rows - row); - QStringList removedCollections = stringList().mid(row, count); + if (count < 1) + return false; - bool itemsRemoved = Super::removeRows(row, count, parent); - if (itemsRemoved) { - emit collectionsRemoved(removedCollections); - if (m_selectedIndex >= row) - selectCollectionIndex(m_selectedIndex - count, true); - } + QStringList removedCollections = m_data.mid(row, count); - return itemsRemoved; + beginRemoveRows(parent, row, row + count - 1); + m_data.remove(row, count); + endRemoveRows(); + + emit collectionsRemoved(removedCollections); + if (m_selectedIndex >= row) + selectCollectionIndex(m_selectedIndex - count, true); + + return true; } QVariant CollectionListModel::data(const QModelIndex &index, int role) const @@ -103,13 +112,21 @@ QVariant CollectionListModel::data(const QModelIndex &index, int role) const switch (role) { case IdRole: return index.row(); - case NameRole: - return Super::data(index); case SelectedRole: return index.row() == m_selectedIndex; + case NameRole: + default: + return m_data.at(index.row()); } +} - return Super::data(index, role); +void CollectionListModel::resetModelData(const QStringList &collectionsList) +{ + QString prevSelectedCollection = selectedIndex() > -1 ? m_data.at(selectedIndex()) : QString(); + beginResetModel(); + m_data = collectionsList; + endResetModel(); + selectCollectionName(prevSelectedCollection); } int CollectionListModel::selectedIndex() const @@ -129,12 +146,17 @@ QString CollectionListModel::sourceAddress() const bool CollectionListModel::contains(const QString &collectionName) const { - return stringList().contains(collectionName); + return m_data.contains(collectionName); +} + +QStringList CollectionListModel::collections() const +{ + return m_data; } void CollectionListModel::selectCollectionIndex(int idx, bool selectAtLeastOne) { - int collectionCount = stringList().size(); + int collectionCount = m_data.size(); int preferredIndex = -1; if (collectionCount) { if (selectAtLeastOne) @@ -148,7 +170,7 @@ void CollectionListModel::selectCollectionIndex(int idx, bool selectAtLeastOne) void CollectionListModel::selectCollectionName(const QString &collectionName) { - int idx = stringList().indexOf(collectionName); + int idx = m_data.indexOf(collectionName); if (idx > -1) selectCollectionIndex(idx); } @@ -158,6 +180,19 @@ QString CollectionListModel::collectionNameAt(int idx) const return index(idx).data(NameRole).toString(); } +void CollectionListModel::addCollection(const QString &collectionName) +{ + if (m_data.contains(collectionName)) + return; + + int row = rowCount(); + beginInsertRows({}, row, row); + m_data.append(collectionName); + endInsertRows(); + + emit collectionAdded(collectionName); +} + void CollectionListModel::setSelectedIndex(int idx) { idx = (idx > -1 && idx < rowCount()) ? idx : -1; @@ -180,7 +215,7 @@ void CollectionListModel::setSelectedIndex(int idx) void CollectionListModel::updateEmpty() { - bool isEmptyNow = stringList().isEmpty(); + bool isEmptyNow = m_data.isEmpty(); if (m_isEmpty != isEmptyNow) { m_isEmpty = isEmptyNow; emit isEmptyChanged(m_isEmpty); diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.h b/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.h index c65af750d80..092dd1d6dc7 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.h @@ -3,14 +3,14 @@ #pragma once +#include #include -#include #include "modelnode.h" namespace QmlDesigner { -class CollectionListModel : public QStringListModel +class CollectionListModel : public QAbstractListModel { Q_OBJECT @@ -22,37 +22,44 @@ public: enum Roles { IdRole = Qt::UserRole + 1, NameRole, SourceRole, SelectedRole, CollectionsRole }; explicit CollectionListModel(const ModelNode &sourceModel); - virtual QHash roleNames() const override; + QHash roleNames() const override; + int rowCount(const QModelIndex &parent = QModelIndex()) const override; bool setData(const QModelIndex &index, const QVariant &value, int role) override; bool removeRows(int row, int count, const QModelIndex &parent = {}) override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + void resetModelData(const QStringList &collectionsList); Q_INVOKABLE int selectedIndex() const; Q_INVOKABLE ModelNode sourceNode() const; Q_INVOKABLE QString sourceAddress() const; Q_INVOKABLE bool contains(const QString &collectionName) const; + Q_INVOKABLE QStringList collections() const; void selectCollectionIndex(int idx, bool selectAtLeastOne = false); void selectCollectionName(const QString &collectionName); QString collectionNameAt(int idx) const; + void addCollection(const QString &collectionName); signals: void selectedIndexChanged(int idx); void isEmptyChanged(bool); void collectionNameChanged(const QString &oldName, const QString &newName); void collectionsRemoved(const QStringList &names); + void collectionAdded(const QString &name); private: void setSelectedIndex(int idx); void updateEmpty(); - using Super = QStringListModel; + using Super = QAbstractListModel; int m_selectedIndex = -1; bool m_isEmpty = false; const ModelNode m_sourceNode; const QString m_sourceType; + + QStringList m_data; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.cpp index a76d4a0b24e..13accfba0e0 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.cpp @@ -56,13 +56,13 @@ QSharedPointer loadCollection( if (document.isObject()) { const QJsonObject sourceObject = document.object(); - collectionsList->setStringList(sourceObject.toVariantMap().keys()); + collectionsList->resetModelData(sourceObject.toVariantMap().keys()); } } else if (sourceNode.type() == CSVCOLLECTIONMODEL_TYPENAME) { QmlDesigner::VariantProperty collectionNameProperty = sourceNode.variantProperty( "objectName"); setupCollectionList(); - collectionsList->setStringList({collectionNameProperty.value().toString()}); + collectionsList->resetModelData({collectionNameProperty.value().toString()}); } return collectionsList; } @@ -209,7 +209,7 @@ void CollectionSourceModel::setSources(const ModelNodes &sources) auto loadedCollection = loadCollection(collectionSource); m_collectionList.append(loadedCollection); - registerCollection(loadedCollection); + registerCollectionList(loadedCollection); } updateEmpty(); @@ -242,7 +242,7 @@ void CollectionSourceModel::addSource(const ModelNode &node) auto loadedCollection = loadCollection(node); m_collectionList.append(loadedCollection); - registerCollection(loadedCollection); + registerCollectionList(loadedCollection); updateEmpty(); endInsertRows(); @@ -353,6 +353,26 @@ CollectionListModel *CollectionSourceModel::selectedCollectionList() return idx.data(CollectionsRole).value(); } +QString CollectionSourceModel::generateCollectionName(const ModelNode &node, + const QString &baseCollectionName) const +{ + int idx = sourceIndex(node); + if (idx < 0) + return {}; + + auto collections = m_collectionList.at(idx); + if (collections.isNull()) + return {}; + + const int maxNumber = std::numeric_limits::max(); + for (int i = 1; i < maxNumber; ++i) { + const QString name = QLatin1String("%1_%2").arg(baseCollectionName).arg(i); + if (!collections->contains(name)) + return name; + } + return {}; +} + void CollectionSourceModel::selectSourceIndex(int idx, bool selectAtLeastOne) { int collectionCount = m_collectionSources.size(); @@ -367,6 +387,21 @@ void CollectionSourceModel::selectSourceIndex(int idx, bool selectAtLeastOne) setSelectedIndex(preferredIndex); } +void CollectionSourceModel::selectCollection(const QVariant &node, const QString &collectionName) +{ + const ModelNode sourceNode = node.value(); + const QModelIndex index = indexOfNode(sourceNode); + if (!index.isValid()) + return; + + selectSource(sourceNode); + auto collections = m_collectionList.at(index.row()); + if (collections.isNull()) + return; + + collections->selectCollectionName(collectionName); +} + void CollectionSourceModel::deselect() { setSelectedIndex(-1); @@ -416,7 +451,8 @@ void CollectionSourceModel::onSelectedCollectionChanged(CollectionListModel *col } void CollectionSourceModel::onCollectionNameChanged(CollectionListModel *collectionList, - const QString &oldName, const QString &newName) + const QString &oldName, + const QString &newName) { using Utils::FilePath; using Utils::FileReader; @@ -657,13 +693,14 @@ void CollectionSourceModel::updateCollectionList(QModelIndex index) if (oldList != newList) { m_collectionList.replace(index.row(), newList); emit dataChanged(index, index, {CollectionsRole}); - registerCollection(newList); + registerCollectionList(newList); } } -void CollectionSourceModel::registerCollection(const QSharedPointer &collection) +void CollectionSourceModel::registerCollectionList( + const QSharedPointer &sharedCollectionList) { - CollectionListModel *collectionList = collection.data(); + CollectionListModel *collectionList = sharedCollectionList.data(); if (collectionList == nullptr) return; @@ -688,10 +725,14 @@ void CollectionSourceModel::registerCollection(const QSharedPointercollections()); + }); } if (collectionList->sourceNode().isValid()) - emit collectionNamesInitialized(collection->stringList()); + emit collectionNamesInitialized(collectionList->collections()); } QModelIndex CollectionSourceModel::indexOfNode(const ModelNode &node) const diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.h b/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.h index ac01c0de22b..1909b77c9ab 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.h @@ -59,8 +59,10 @@ public: ModelNode sourceNodeAt(int idx); CollectionListModel *selectedCollectionList(); + QString generateCollectionName(const ModelNode &node, const QString &baseCollectionName) const; Q_INVOKABLE void selectSourceIndex(int idx, bool selectAtLeastOne = false); + Q_INVOKABLE void selectCollection(const QVariant &node, const QString &collectionName); Q_INVOKABLE void deselect(); Q_INVOKABLE void updateSelectedSource(bool selectAtLeastOne = false); Q_INVOKABLE bool collectionExists(const QVariant &node, const QString &collectionName) const; @@ -90,7 +92,7 @@ private: void setSelectedCollectionName(const QString &collectionName); void updateEmpty(); void updateCollectionList(QModelIndex index); - void registerCollection(const QSharedPointer &collection); + void registerCollectionList(const QSharedPointer &collectionList); QModelIndex indexOfNode(const ModelNode &node) const; using Super = QAbstractListModel; diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp index 9933ac38179..9e9405be3d2 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp @@ -12,6 +12,7 @@ #include "designmodecontext.h" #include "nodeabstractproperty.h" #include "nodemetainfo.h" +#include "nodeproperty.h" #include "qmldesignerplugin.h" #include "variantproperty.h" @@ -192,6 +193,17 @@ void CollectionView::selectedNodesChanged(const QList &selectedNodeLi } } +void CollectionView::customNotification(const AbstractView *, + const QString &identifier, + const QList &nodeList, + const QList &data) +{ + if (identifier == QLatin1String("item_library_created_by_drop") && !nodeList.isEmpty()) + onItemLibraryNodeCreated(nodeList.first()); + else if (identifier == QLatin1String("open_collection_by_id") && !data.isEmpty()) + m_widget->openCollection(collectionNameFromDataStoreChildren(data.first().toByteArray())); +} + void CollectionView::addResource(const QUrl &url, const QString &name, const QString &type) { executeInTransaction(Q_FUNC_INFO, [this, &url, &name, &type]() { @@ -221,12 +233,11 @@ void CollectionView::addResource(const QUrl &url, const QString &name, const QSt }); } -void CollectionView::assignCollectionToSelectedNode(const QString &collectionName) +void CollectionView::assignCollectionToNode(const QString &collectionName, const ModelNode &node) { - QTC_ASSERT(dataStoreNode() && hasSingleSelectedModelNode(), return); m_dataStore->assignCollectionToNode( this, - singleSelectedModelNode(), + node, collectionName, [&](const QString &collectionName, const QString &columnName) -> bool { const CollectionReference reference{dataStoreNode(), collectionName}; @@ -238,6 +249,12 @@ void CollectionView::assignCollectionToSelectedNode(const QString &collectionNam }); } +void CollectionView::assignCollectionToSelectedNode(const QString &collectionName) +{ + QTC_ASSERT(dataStoreNode() && hasSingleSelectedModelNode(), return); + assignCollectionToNode(collectionName, singleSelectedModelNode()); +} + void CollectionView::registerDeclarativeType() { CollectionDetails::registerDeclarativeType(); @@ -255,6 +272,25 @@ ModelNode CollectionView::dataStoreNode() const return m_dataStore->modelNode(); } +void CollectionView::ensureDataStoreExists() +{ + bool filesJustCreated = false; + bool filesExist = CollectionEditorUtils::ensureDataStoreExists(filesJustCreated); + if (filesExist && filesJustCreated) + resetDataStoreNode(); +} + +QString CollectionView::collectionNameFromDataStoreChildren(const PropertyName &childPropertyName) const +{ + return dataStoreNode() + .nodeProperty(childPropertyName) + .modelNode() + .property(CollectionEditorConstants::JSONCHILDMODELNAME_PROPERTY) + .toVariantProperty() + .value() + .toString(); +} + void CollectionView::refreshModel() { if (!model()) @@ -292,4 +328,20 @@ void CollectionView::ensureStudioModelImport() }); } +void CollectionView::onItemLibraryNodeCreated(const ModelNode &node) +{ + ensureDataStoreExists(); + CollectionSourceModel *sourceModel = m_widget->sourceModel(); + + if (node.metaInfo().isQtQuickListView()) { + const QString newCollectionName = sourceModel->generateCollectionName(dataStoreNode(), + "ListModel"); + sourceModel->addCollectionToSource(dataStoreNode(), + newCollectionName, + CollectionEditorUtils::defaultColorCollection()); + assignCollectionToNode(newCollectionName, node); + m_widget->openCollection(newCollectionName); + } +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionview.h b/src/plugins/qmldesigner/components/collectioneditor/collectionview.h index c08368b0c35..db3964726b4 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionview.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionview.h @@ -41,20 +41,29 @@ public: void selectedNodesChanged(const QList &selectedNodeList, const QList &lastSelectedNodeList) override; + void customNotification(const AbstractView *view, + const QString &identifier, + const QList &nodeList, + const QList &data) override; + void addResource(const QUrl &url, const QString &name, const QString &type); + void assignCollectionToNode(const QString &collectionName, const ModelNode &node); void assignCollectionToSelectedNode(const QString &collectionName); static void registerDeclarativeType(); void resetDataStoreNode(); ModelNode dataStoreNode() const; + void ensureDataStoreExists(); + QString collectionNameFromDataStoreChildren(const PropertyName &childPropertyName) const; private: void refreshModel(); NodeMetaInfo jsonCollectionMetaInfo() const; NodeMetaInfo csvCollectionMetaInfo() const; void ensureStudioModelImport(); + void onItemLibraryNodeCreated(const ModelNode &node); QPointer m_widget; std::unique_ptr m_dataStore; diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp index 79d3d563a25..d17cbb5bfd0 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp @@ -9,6 +9,7 @@ #include "collectioneditorutils.h" #include "collectionsourcemodel.h" #include "collectionview.h" +#include "designmodewidget.h" #include "qmldesignerconstants.h" #include "qmldesignerplugin.h" #include "theme.h" @@ -204,7 +205,7 @@ bool CollectionWidget::isValidUrlToImport(const QUrl &url) const bool CollectionWidget::importFile(const QString &collectionName, const QUrl &url) { using Utils::FilePath; - ensureDataStoreExists(); + m_view->ensureDataStoreExists(); const ModelNode node = dataStoreNode(); if (!node.isValid()) { @@ -264,7 +265,7 @@ bool CollectionWidget::importFile(const QString &collectionName, const QUrl &url bool CollectionWidget::addCollectionToDataStore(const QString &collectionName) { - ensureDataStoreExists(); + m_view->ensureDataStoreExists(); const ModelNode node = dataStoreNode(); if (!node.isValid()) { warn(tr("Can not import to the main model"), tr("The default model node is not available.")); @@ -288,12 +289,10 @@ void CollectionWidget::assignCollectionToSelectedNode(const QString collectionNa m_view->assignCollectionToSelectedNode(collectionName); } -void CollectionWidget::ensureDataStoreExists() +void CollectionWidget::openCollection(const QString &collectionName) { - bool filesJustCreated = false; - bool filesExist = CollectionEditorUtils::ensureDataStoreExists(filesJustCreated); - if (filesExist && filesJustCreated) - m_view->resetDataStoreNode(); + m_sourceModel->selectCollection(QVariant::fromValue(m_view->dataStoreNode()), collectionName); + QmlDesignerPlugin::instance()->mainWidget()->showDockWidget("CollectionEditor", true); } ModelNode CollectionWidget::dataStoreNode() const diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h index 23f4ad8dbf3..b4a6578580f 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h @@ -43,7 +43,7 @@ public: Q_INVOKABLE bool importFile(const QString &collectionName, const QUrl &url); Q_INVOKABLE bool addCollectionToDataStore(const QString &collectionName); Q_INVOKABLE void assignCollectionToSelectedNode(const QString collectionName); - Q_INVOKABLE void ensureDataStoreExists(); + Q_INVOKABLE void openCollection(const QString &collectionName); Q_INVOKABLE ModelNode dataStoreNode() const; void warn(const QString &title, const QString &body); diff --git a/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h b/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h index 17ba5aa9707..6a8833ef292 100644 --- a/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h +++ b/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h @@ -69,6 +69,7 @@ const char mergeTemplateCommandId[] = "MergeTemplate"; const char goToImplementationCommandId[] = "GoToImplementation"; const char makeComponentCommandId[] = "MakeComponent"; const char editMaterialCommandId[] = "EditMaterial"; +const char editCollectionCommandId[] = "EditCollection"; const char addItemToStackedContainerCommandId[] = "AddItemToStackedContainer"; const char addTabBarToStackedContainerCommandId[] = "AddTabBarToStackedContainer"; const char increaseIndexOfStackedContainerCommandId[] = "IncreaseIndexOfStackedContainer"; @@ -126,6 +127,7 @@ const char mergeTemplateDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMen const char goToImplementationDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Go to Implementation"); const char makeComponentDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Create Component"); const char editMaterialDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Edit Material"); +const char editCollectionDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Edit Model"); const char editAnnotationsDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Edit Annotations"); const char addMouseAreaFillDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Add Mouse Area"); @@ -209,6 +211,8 @@ enum PrioritiesEnum : int { SelectionCategory, ArrangeCategory, EditCategory, + EditListModel, + EditCollection, /******** Section *****************************/ PositionSection = 2000, SnappingCategory, diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp index 6e4b1a1efd5..6441d11b733 100644 --- a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp +++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp @@ -840,16 +840,22 @@ public: {}, ComponentCoreConstants::rootCategory, QKeySequence("Alt+e"), - 1001, + ComponentCoreConstants::Priorities::EditListModel, &openDialog, - &isListViewInBaseState, - &isListViewInBaseState) + &isListViewInBaseStateAndHasListModel, + &isListViewInBaseStateAndHasListModel) {} - static bool isListViewInBaseState(const SelectionContext &selectionState) + static bool isListViewInBaseStateAndHasListModel(const SelectionContext &selectionState) { - return selectionState.isInBaseState() && selectionState.singleNodeIsSelected() - && selectionState.currentSingleSelectedNode().metaInfo().isListOrGridView(); + if (!selectionState.isInBaseState() || !selectionState.singleNodeIsSelected()) + return false; + + const ModelNode singleSelectedNode = selectionState.currentSingleSelectedNode(); + + return singleSelectedNode.metaInfo().isListOrGridView() + && singleSelectedNode.property("model").toNodeProperty().modelNode().type() + == "QtQml.Models.ListModel"; } bool isEnabled(const SelectionContext &) const override { return true; } @@ -1977,6 +1983,16 @@ void DesignerActionManager::createDefaultDesignerActions() addDesignerAction(new EditListModelAction); + addDesignerAction(new ModelNodeContextMenuAction(editCollectionCommandId, + editCollectionDisplayName, + contextIcon(DesignerIcons::EditIcon), + rootCategory, + QKeySequence("Alt+e"), + ComponentCoreConstants::Priorities::EditCollection, + &editCollection, + &hasCollectionAsModel, + &hasCollectionAsModel)); + addDesignerAction(new ModelNodeContextMenuAction(openSignalDialogCommandId, openSignalDialogDisplayName, {}, diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h b/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h index 6bc9c703bc8..e7224f9edd4 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h +++ b/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h @@ -69,6 +69,18 @@ inline bool modelHasMaterial(const SelectionContext &selectionState) return prop.exists() && (!prop.expression().isEmpty() || !prop.resolveToModelNodeList().empty()); } +inline bool hasCollectionAsModel(const SelectionContext &selectionState) +{ + if (!selectionState.isInBaseState() || !selectionState.singleNodeIsSelected()) + return false; + + const ModelNode singleSelectedNode = selectionState.currentSingleSelectedNode(); + + return singleSelectedNode.metaInfo().isQtQuickListView() + && singleSelectedNode.property("model").toBindingProperty().expression().startsWith( + "DataStore."); +} + inline bool selectionEnabled(const SelectionContext &selectionState) { return selectionState.showSelectionTools(); diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp index ae8f8790170..98e0e4aef59 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp @@ -833,6 +833,30 @@ void editMaterial(const SelectionContext &selectionContext) } } +// Open a collection in the collection editor +void editCollection(const SelectionContext &selectionContext) +{ + ModelNode modelNode = selectionContext.targetNode(); + + if (!modelNode) + modelNode = selectionContext.currentSingleSelectedNode(); + + if (!modelNode) + return; + + const QString dataStoreExpression = "DataStore."; + + BindingProperty prop = modelNode.bindingProperty("model"); + if (!prop.exists() || !prop.expression().startsWith(dataStoreExpression)) + return; + + AbstractView *view = selectionContext.view(); + const QString collectionId = prop.expression().mid(dataStoreExpression.size()); + + // to CollectionEditor... + view->emitCustomNotification("open_collection_by_id", {}, {collectionId}); +} + void addItemToStackedContainer(const SelectionContext &selectionContext) { AbstractView *view = selectionContext.view(); diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h index 196dd4922c8..4556462f30b 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h @@ -92,6 +92,7 @@ void layoutGridLayout(const SelectionContext &selectionState); void goImplementation(const SelectionContext &selectionState); void addNewSignalHandler(const SelectionContext &selectionState); void editMaterial(const SelectionContext &selectionContext); +void editCollection(const SelectionContext &selectionContext); void addSignalHandlerOrGotoImplementation(const SelectionContext &selectionState, bool addAlwaysNewSlot); void removeLayout(const SelectionContext &selectionContext); void removePositioner(const SelectionContext &selectionContext); diff --git a/src/plugins/qmldesigner/components/formeditor/dragtool.cpp b/src/plugins/qmldesigner/components/formeditor/dragtool.cpp index e363e9bb11d..0b7d199b500 100644 --- a/src/plugins/qmldesigner/components/formeditor/dragtool.cpp +++ b/src/plugins/qmldesigner/components/formeditor/dragtool.cpp @@ -274,6 +274,11 @@ void DragTool::dropEvent(const QList &itemList, QGraphicsSceneD nodeList.append(node); } view()->setSelectedModelNodes(nodeList); + + bool itemLibraryJustCreated = hasItemLibraryInfo(event->mimeData()) + && nodeList.size() == 1; + if (itemLibraryJustCreated) + view()->emitCustomNotification("item_library_created_by_drop", nodeList); } m_dragNodes.clear(); } @@ -382,7 +387,7 @@ void DragTool::dragMoveEvent(const QList &itemList, QGraphicsSc } } -void DragTool::end() +void DragTool::end() { m_moveManipulator.end(); clear(); diff --git a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp index 7cf02c72592..5ae3d5d7207 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp +++ b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp @@ -796,8 +796,10 @@ void NavigatorTreeModel::handleItemLibraryItemDrop(const QMimeData *mimeData, in moveNodesInteractive(targetProperty, newModelNodeList, targetRowNumber); } - if (newQmlObjectNode.isValid()) + if (newQmlObjectNode.isValid()) { m_view->setSelectedModelNode(newQmlObjectNode.modelNode()); + m_view->emitCustomNotification("item_library_created_by_drop", {newQmlObjectNode}); + } } } } diff --git a/src/plugins/qmldesigner/designercore/include/nodemetainfo.h b/src/plugins/qmldesigner/designercore/include/nodemetainfo.h index 58cdeba228e..7e171c54450 100644 --- a/src/plugins/qmldesigner/designercore/include/nodemetainfo.h +++ b/src/plugins/qmldesigner/designercore/include/nodemetainfo.h @@ -166,6 +166,7 @@ public: bool isQtQuick3DLight() const; bool isQtQuickListElement() const; bool isQtQuickListModel() const; + bool isQtQuickListView() const; bool isQtQuick3DMaterial() const; bool isQtQuick3DModel() const; bool isQtQuick3DNode() const; diff --git a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp index c695f488b1f..975b12ddd1e 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp @@ -2766,6 +2766,16 @@ bool NodeMetaInfo::isQtQuickListModel() const } } +bool NodeMetaInfo::isQtQuickListView() const +{ + if constexpr (useProjectStorage()) { + using namespace Storage::Info; + return isBasedOnCommonType(m_projectStorage, m_typeId); + } else { + return isValid() && (isSubclassOf("QtQuick.ListView")); + } +} + bool NodeMetaInfo::isQtQuick3DInstanceList() const { if constexpr (useProjectStorage()) { From cae86d697787f2deb1f793b171c0036ef0cecef1 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Tue, 13 Feb 2024 17:00:50 +0200 Subject: [PATCH 068/176] QmlDesigner: Create proper ListView delegate for assigned model Task-number: QDS-11776 Change-Id: I210a9803725b59a7cfe7887908922c38d969bc2d Reviewed-by: Miikka Heikkinen Reviewed-by: Qt CI Patch Build Bot --- .../collectioneditor/collectionview.cpp | 72 +++++++++++++++---- 1 file changed, 60 insertions(+), 12 deletions(-) diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp index 9e9405be3d2..92a66e51bec 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp @@ -37,6 +37,22 @@ inline bool isStudioCollectionModel(const QmlDesigner::ModelNode &node) || node.metaInfo().typeName() == CSVCOLLECTIONMODEL_TYPENAME; } +inline void setVariantPropertyValue(const QmlDesigner::ModelNode &node, + const QmlDesigner::PropertyName &propertyName, + const QVariant &value) +{ + QmlDesigner::VariantProperty property = node.variantProperty(propertyName); + property.setValue(value); +} + +inline void setBindingPropertyExpression(const QmlDesigner::ModelNode &node, + const QmlDesigner::PropertyName &propertyName, + const QString &expression) +{ + QmlDesigner::BindingProperty property = node.bindingProperty(propertyName); + property.setExpression(expression); +} + } // namespace namespace QmlDesigner { @@ -235,18 +251,50 @@ void CollectionView::addResource(const QUrl &url, const QString &name, const QSt void CollectionView::assignCollectionToNode(const QString &collectionName, const ModelNode &node) { - m_dataStore->assignCollectionToNode( - this, - node, - collectionName, - [&](const QString &collectionName, const QString &columnName) -> bool { - const CollectionReference reference{dataStoreNode(), collectionName}; - return m_widget->collectionDetailsModel()->collectionHasColumn(reference, columnName); - }, - [&](const QString &collectionName) -> QString { - const CollectionReference reference{dataStoreNode(), collectionName}; - return m_widget->collectionDetailsModel()->getFirstColumnName(reference); - }); + using DataType = CollectionDetails::DataType; + executeInTransaction("CollectionView::assignCollectionToNode", [&]() { + m_dataStore->assignCollectionToNode( + this, + node, + collectionName, + [&](const QString &collectionName, const QString &columnName) -> bool { + const CollectionReference reference{dataStoreNode(), collectionName}; + return m_widget->collectionDetailsModel()->collectionHasColumn(reference, columnName); + }, + [&](const QString &collectionName) -> QString { + const CollectionReference reference{dataStoreNode(), collectionName}; + return m_widget->collectionDetailsModel()->getFirstColumnName(reference); + }); + + // Create and assign a delegate to the list view item + if (node.metaInfo().isQtQuickListView()) { + CollectionDetails collection = m_widget->collectionDetailsModel()->upToDateConstCollection( + {dataStoreNode(), collectionName}); + + ModelNode rowItem(createModelNode("QtQuick.Row")); + ::setVariantPropertyValue(rowItem, "spacing", 5); + + const int columnsCount = collection.columns(); + for (int column = 0; column < columnsCount; ++column) { + const DataType dataType = collection.typeAt(column); + const QString columnName = collection.propertyAt(column); + ModelNode cellItem; + if (dataType == DataType::Color) { + cellItem = createModelNode("QtQuick.Rectangle"); + ::setBindingPropertyExpression(cellItem, "color", columnName); + ::setVariantPropertyValue(cellItem, "height", 20); + } else { + cellItem = createModelNode("QtQuick.Text"); + ::setBindingPropertyExpression(cellItem, "text", columnName); + } + ::setVariantPropertyValue(cellItem, "width", 100); + rowItem.defaultNodeAbstractProperty().reparentHere(cellItem); + } + + NodeProperty delegateProperty = node.nodeProperty("delegate"); + delegateProperty.setModelNode(rowItem); + } + }); } void CollectionView::assignCollectionToSelectedNode(const QString &collectionName) From 3e81485c6aed851ffe0eedc783e01d6758132a37 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Mon, 19 Feb 2024 15:18:24 +0200 Subject: [PATCH 069/176] QmlDesigner: Fix the bug for dropping ListView when DataStore is missing * Creating ListView will be postponed when the dataStore is just created so that the model manager can update the information of DataStore. * The initial delegate and model for the listview are faked in order to show the same shape before and after the assignment. Change-Id: I45ac7486890556136ca98fc131f90896efc3b839 Reviewed-by: Mahmoud Badri --- .../collectioneditorutils.cpp | 6 -- .../collectioneditor/collectionview.cpp | 99 +++++++++++++++++-- .../collectioneditor/collectionview.h | 31 ++++++ .../collectioneditor/datastoremodelnode.cpp | 8 ++ .../qtquickplugin/source/listview.qml | 40 ++++---- .../qtquickplugin/source/listviewv2.qml | 40 ++++---- 6 files changed, 169 insertions(+), 55 deletions(-) diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp index fc925126350..777de1c3357 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp @@ -347,12 +347,6 @@ bool ensureDataStoreExists(bool &justCreated) if (qmlDirSaver.finalize()) { justCreated = true; - - // Force code model reset to notice changes to existing module - auto modelManager = QmlJS::ModelManagerInterface::instance(); - if (modelManager) - modelManager->resetCodeModel(); - return true; } diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp index 92a66e51bec..df4fee7a7ee 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp @@ -19,15 +19,16 @@ #include #include #include - -#include -#include -#include +#include #include #include #include +#include +#include +#include + namespace { inline bool isStudioCollectionModel(const QmlDesigner::ModelNode &node) @@ -139,6 +140,12 @@ void CollectionView::modelAttached(Model *model) resetDataStoreNode(); } +void CollectionView::modelAboutToBeDetached([[maybe_unused]] Model *model) +{ + m_libraryInfoIsUpdated = false; + disconnect(m_documentUpdateConnection); +} + void CollectionView::nodeReparented(const ModelNode &node, [[maybe_unused]] const NodeAbstractProperty &newPropertyParent, [[maybe_unused]] const NodeAbstractProperty &oldPropertyParent, @@ -292,6 +299,10 @@ void CollectionView::assignCollectionToNode(const QString &collectionName, const } NodeProperty delegateProperty = node.nodeProperty("delegate"); + // Remove the old model node if is available + if (delegateProperty.modelNode()) + delegateProperty.modelNode().destroy(); + delegateProperty.setModelNode(rowItem); } }); @@ -324,8 +335,29 @@ void CollectionView::ensureDataStoreExists() { bool filesJustCreated = false; bool filesExist = CollectionEditorUtils::ensureDataStoreExists(filesJustCreated); - if (filesExist && filesJustCreated) - resetDataStoreNode(); + 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; + } + } } QString CollectionView::collectionNameFromDataStoreChildren(const PropertyName &childPropertyName) const @@ -387,9 +419,62 @@ void CollectionView::onItemLibraryNodeCreated(const ModelNode &node) sourceModel->addCollectionToSource(dataStoreNode(), newCollectionName, CollectionEditorUtils::defaultColorCollection()); - assignCollectionToNode(newCollectionName, node); m_widget->openCollection(newCollectionName); + new DelayedAssignCollectionToItem(this, node, newCollectionName); } } +void CollectionView::onDocumentUpdated(const QSharedPointer &doc) +{ + if (m_expectedDocumentUpdates.contains(doc->fileName())) + m_expectedDocumentUpdates.remove(doc->fileName()); + + if (m_expectedDocumentUpdates.isEmpty()) { + disconnect(m_documentUpdateConnection); + m_libraryInfoIsUpdated = true; + } +} + +DelayedAssignCollectionToItem::DelayedAssignCollectionToItem(CollectionView *parent, + const ModelNode &node, + const QString &collectionName) + : QObject(parent) + , m_collectionView(parent) + , m_node(node) + , m_name(collectionName) +{ + checkAndAssign(); +} + +void DelayedAssignCollectionToItem::checkAndAssign() +{ + AbstractView *view = m_node.view(); + + if (!m_node || !m_collectionView || !view || ++m_counter > 50) { + deleteLater(); + return; + } + + bool dataStoreFound = false; + + if (m_collectionView->isDataStoreReady()) { + for (const QmlTypeData &cppTypeData : view->rewriterView()->getQMLTypes()) { + if (cppTypeData.isSingleton && cppTypeData.typeName == "DataStore") + dataStoreFound = true; + } + if (!dataStoreFound && !m_rewriterAmended) { + m_collectionView->model()->rewriterView()->forceAmend(); + m_rewriterAmended = true; + } + } + + if (!dataStoreFound) { + QTimer::singleShot(100, this, &DelayedAssignCollectionToItem::checkAndAssign); + return; + } + + m_collectionView->assignCollectionToNode(m_name, m_node); + deleteLater(); +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionview.h b/src/plugins/qmldesigner/components/collectioneditor/collectionview.h index db3964726b4..649b37f3beb 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionview.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionview.h @@ -7,6 +7,9 @@ #include "datastoremodelnode.h" #include "modelnode.h" +namespace QmlJS { +class Document; +} namespace QmlDesigner { class CollectionWidget; @@ -23,6 +26,7 @@ public: WidgetInfo widgetInfo() override; void modelAttached(Model *model) override; + void modelAboutToBeDetached(Model *model) override; void nodeReparented(const ModelNode &node, const NodeAbstractProperty &newPropertyParent, @@ -58,14 +62,41 @@ public: void ensureDataStoreExists(); QString collectionNameFromDataStoreChildren(const PropertyName &childPropertyName) const; + bool isDataStoreReady() const { return m_libraryInfoIsUpdated; } + private: void refreshModel(); NodeMetaInfo jsonCollectionMetaInfo() const; NodeMetaInfo csvCollectionMetaInfo() const; void ensureStudioModelImport(); void onItemLibraryNodeCreated(const ModelNode &node); + void onDocumentUpdated(const QSharedPointer &doc); QPointer m_widget; std::unique_ptr m_dataStore; + QSet m_expectedDocumentUpdates; + QMetaObject::Connection m_documentUpdateConnection; + bool m_libraryInfoIsUpdated = false; }; + +class DelayedAssignCollectionToItem : public QObject +{ + Q_OBJECT + +public: + DelayedAssignCollectionToItem(CollectionView *parent, + const ModelNode &node, + const QString &collectionName); + +public slots: + void checkAndAssign(); + +private: + QPointer m_collectionView; + ModelNode m_node; + QString m_name; + int m_counter = 0; + bool m_rewriterAmended = false; +}; + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.cpp b/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.cpp index e5ddb1dd3a0..7a7a2f7d7a6 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.cpp @@ -459,8 +459,16 @@ void DataStoreModelNode::assignCollectionToNode(AbstractView *view, view->executeInTransaction("assignCollectionToNode", [&]() { QString identifier = QString("DataStore.%1").arg(QString::fromLatin1(sourceProperty.name())); + + // Remove the old model node property if exists + NodeProperty modelNodeProperty = targetNode.nodeProperty("model"); + if (modelNodeProperty.modelNode()) + modelNodeProperty.modelNode().destroy(); + + // Assign the collection to the node BindingProperty modelProperty = targetNode.bindingProperty("model"); modelProperty.setExpression(identifier); + if (CollectionEditorUtils::hasTextRoleProperty(targetNode)) { VariantProperty textRoleProperty = targetNode.variantProperty("textRole"); const QVariant currentTextRoleValue = textRoleProperty.value(); diff --git a/src/plugins/qmldesigner/qtquickplugin/source/listview.qml b/src/plugins/qmldesigner/qtquickplugin/source/listview.qml index c76d9c535e2..ffda2e309e7 100644 --- a/src/plugins/qmldesigner/qtquickplugin/source/listview.qml +++ b/src/plugins/qmldesigner/qtquickplugin/source/listview.qml @@ -4,40 +4,38 @@ import QtQuick 1.0 ListView { - width: 110 - height: 160 + width: 160 + height: 80 model: ListModel { - ListElement { - name: "Grey" - colorCode: "grey" - } ListElement { name: "Red" colorCode: "red" } + ListElement { + name: "Green" + colorCode: "green" + } ListElement { name: "Blue" colorCode: "blue" } ListElement { - name: "Green" - colorCode: "green" + name: "White" + colorCode: "white" } } - delegate: Item { - width: 80 - height: 40 - x: 5 - Row { - id: row1 - spacing: 10 - Rectangle { width: 40; height: 40; color: colorCode; } - Text { - text: name - anchors.verticalCenter: parent.verticalCenter - font.bold: true - } + delegate: Row { + spacing: 5 + Rectangle { + width: 100 + height: 20 + color: colorCode + } + + Text { + width: 100 + text: name } } } diff --git a/src/plugins/qmldesigner/qtquickplugin/source/listviewv2.qml b/src/plugins/qmldesigner/qtquickplugin/source/listviewv2.qml index a11e3ee68da..20b08f7cfcf 100644 --- a/src/plugins/qmldesigner/qtquickplugin/source/listviewv2.qml +++ b/src/plugins/qmldesigner/qtquickplugin/source/listviewv2.qml @@ -4,40 +4,38 @@ import QtQuick 2.0 ListView { - width: 110 - height: 160 + width: 160 + height: 80 model: ListModel { - ListElement { - name: "Grey" - colorCode: "grey" - } ListElement { name: "Red" colorCode: "red" } + ListElement { + name: "Green" + colorCode: "green" + } ListElement { name: "Blue" colorCode: "blue" } ListElement { - name: "Green" - colorCode: "green" + name: "White" + colorCode: "white" } } - delegate: Item { - width: 80 - height: 40 - x: 5 - Row { - id: row1 - spacing: 10 - Rectangle { width: 40; height: 40; color: colorCode; } - Text { - text: name - anchors.verticalCenter: parent.verticalCenter - font.bold: true - } + delegate: Row { + spacing: 5 + Rectangle { + width: 100 + height: 20 + color: colorCode + } + + Text { + width: 100 + text: name } } } From f2b29195da0bf6db27c693583618ca3efefa707a Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Tue, 20 Feb 2024 13:55:00 +0200 Subject: [PATCH 070/176] QmlDesigner: Disable "Edit Component" for effect composer effects These components are autogenerated and overwritten by effect composer, so a regular user has no reason to modify them manually. Fixes: QDS-9020 Change-Id: Ida6706f123a8304944a19364c59620b8d9b4adbb Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri --- src/plugins/effectcomposer/effectcomposermodel.cpp | 1 + .../components/componentcore/designeractionmanager.cpp | 2 +- .../componentcore/modelnodecontextmenu_helper.cpp | 6 +++--- .../components/componentcore/modelnodecontextmenu_helper.h | 2 +- .../components/propertyeditor/propertyeditorqmlbackend.cpp | 3 ++- .../components/propertyeditor/propertyeditorview.cpp | 4 +++- 6 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/plugins/effectcomposer/effectcomposermodel.cpp b/src/plugins/effectcomposer/effectcomposermodel.cpp index 6d843b36175..be64cfceead 100644 --- a/src/plugins/effectcomposer/effectcomposermodel.cpp +++ b/src/plugins/effectcomposer/effectcomposermodel.cpp @@ -577,6 +577,7 @@ QString EffectComposerModel::getQmlEffectString() QString header{ R"( // Created with Qt Design Studio (version %1), %2 +// Do not manually edit this file, it will be overwritten if effect is modified in Qt Design Studio. import QtQuick diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp index 6441d11b733..8dba5b90a41 100644 --- a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp +++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp @@ -1896,7 +1896,7 @@ void DesignerActionManager::createDefaultDesignerActions() QKeySequence(Qt::Key_F2), Priorities::ComponentActions + 2, &goIntoComponentOperation, - &selectionIsComponent)); + &selectionIsEditableComponent)); addDesignerAction(new ModelNodeContextMenuAction(jumpToCodeCommandId, JumpToCodeDisplayName, diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.cpp b/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.cpp index 4a119d88a53..f6e18458b2f 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.cpp +++ b/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.cpp @@ -89,10 +89,10 @@ bool fileComponentExists(const ModelNode &modelNode) return QFileInfo::exists(fileName); } -bool selectionIsComponent(const SelectionContext &selectionState) +bool selectionIsEditableComponent(const SelectionContext &selectionState) { - return selectionState.currentSingleSelectedNode().isComponent() - && fileComponentExists(selectionState.currentSingleSelectedNode()); + ModelNode node = selectionState.currentSingleSelectedNode(); + return node.isComponent() && !QmlItemNode(node).isEffectItem() && fileComponentExists(node); } bool selectionIsImported3DAsset(const SelectionContext &selectionState) diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h b/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h index e7224f9edd4..593ace34d14 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h +++ b/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h @@ -124,7 +124,7 @@ inline bool singleSelectedItem(const SelectionContext &selectionState) } bool selectionHasSameParent(const SelectionContext &selectionState); -bool selectionIsComponent(const SelectionContext &selectionState); +bool selectionIsEditableComponent(const SelectionContext &selectionState); bool singleSelectionItemIsAnchored(const SelectionContext &selectionState); bool singleSelectionItemIsNotAnchored(const SelectionContext &selectionState); bool selectionIsImported3DAsset(const SelectionContext &selectionState); diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp index 393183f57c4..ab988140f59 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp @@ -749,7 +749,8 @@ QString PropertyEditorQmlBackend::templateGeneration(const NodeMetaInfo &metaTyp qmlTemplate += "Column {\n"; qmlTemplate += "width: parent.width\n"; - if (node.modelNode().isComponent()) + bool isEditableComponent = node.modelNode().isComponent() && !QmlItemNode(node).isEffectItem(); + if (isEditableComponent) qmlTemplate += "ComponentButton {}\n"; QString qmlInnerTemplate = ""; diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp index 91e62bfef35..98901682a5e 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp @@ -574,8 +574,10 @@ void PropertyEditorView::setupQmlBackend() { if constexpr (useProjectStorage()) { auto selfAndPrototypes = m_selectedNode.metaInfo().selfAndPrototypes(); + bool isEditableComponent = m_selectedNode.isComponent() + && !QmlItemNode(m_selectedNode).isEffectItem(); auto specificQmlData = m_propertyEditorComponentGenerator.create(selfAndPrototypes, - m_selectedNode.isComponent()); + isEditableComponent); auto [panePath, specificsPath] = findPaneAndSpecificsPath(selfAndPrototypes, model()->pathCache()); PropertyEditorQmlBackend *currentQmlBackend = getQmlBackend(m_qmlBackendHash, From 5e4c1ba5a05a5855845e50f61aeb92df093fc950 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Mon, 19 Feb 2024 12:43:22 +0100 Subject: [PATCH 071/176] QmlDesigner: Cleanup notifier The notifier should not change the state. That is really surprising. Change-Id: I179cf9e49c6ae916435c9aada5e1ddff6f9f52ee Reviewed-by: Tim Jenssen Reviewed-by: Qt CI Patch Build Bot --- .../qmldesigner/designercore/include/model.h | 5 ++++ .../designercore/model/abstractview.cpp | 23 ++++++++----------- .../qmldesigner/designercore/model/model.cpp | 22 ++++++++++++++++-- 3 files changed, 35 insertions(+), 15 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/include/model.h b/src/plugins/qmldesigner/designercore/include/model.h index 9816857db65..c6e1bea1120 100644 --- a/src/plugins/qmldesigner/designercore/include/model.h +++ b/src/plugins/qmldesigner/designercore/include/model.h @@ -245,6 +245,11 @@ public: void startDrag(QMimeData *mimeData, const QPixmap &icon); void endDrag(); + void setCurrentStateNode(const ModelNode &node); + ModelNode currentStateNode(AbstractView *view = nullptr); + + void setCurrentTimeline(const ModelNode &timeline); + NotNullPointer projectStorage() const; const PathCacheType &pathCache() const; PathCacheType &pathCache(); diff --git a/src/plugins/qmldesigner/designercore/model/abstractview.cpp b/src/plugins/qmldesigner/designercore/model/abstractview.cpp index ff332ee3b2d..32bf050a2fa 100644 --- a/src/plugins/qmldesigner/designercore/model/abstractview.cpp +++ b/src/plugins/qmldesigner/designercore/model/abstractview.cpp @@ -569,8 +569,8 @@ void AbstractView::setCurrentTimeline(const ModelNode &timeline) if (currentTimeline().isValid()) currentTimeline().toogleRecording(false); - if (model()) - model()->d->notifyCurrentTimelineChanged(timeline); + if (m_model) + m_model->setCurrentTimeline(timeline); } void AbstractView::activateTimelineRecording(const ModelNode &timeline) @@ -580,8 +580,8 @@ void AbstractView::activateTimelineRecording(const ModelNode &timeline) Internal::WriteLocker locker(m_model.data()); - if (model()) - model()->d->notifyCurrentTimelineChanged(timeline); + if (m_model) + m_model->setCurrentTimeline(timeline); } void AbstractView::deactivateTimelineRecording() @@ -590,9 +590,8 @@ void AbstractView::deactivateTimelineRecording() currentTimeline().toogleRecording(false); currentTimeline().resetGroupRecording(); } - - if (model()) - model()->d->notifyCurrentTimelineChanged(ModelNode()); + if (m_model) + m_model->setCurrentTimeline({}); } bool AbstractView::executeInTransaction(const QByteArray &identifier, const OperationBlock &lambda) @@ -761,9 +760,8 @@ void AbstractView::emitRewriterEndTransaction() void AbstractView::setCurrentStateNode(const ModelNode &node) { - Internal::WriteLocker locker(m_model.data()); - if (model()) - model()->d->notifyCurrentStateChanged(node); + if (m_model) + m_model->setCurrentStateNode(node); } void AbstractView::changeRootNodeType(const TypeName &type, int majorVersion, int minorVersion) @@ -872,11 +870,10 @@ ModelNode AbstractView::getTextureDefaultInstance(const QString &source) return {}; } - ModelNode AbstractView::currentStateNode() const { - if (model()) - return ModelNode(m_model.data()->d->currentStateNode(), m_model.data(), const_cast(this)); + if (m_model) + return m_model->currentStateNode(const_cast(this)); return {}; } diff --git a/src/plugins/qmldesigner/designercore/model/model.cpp b/src/plugins/qmldesigner/designercore/model/model.cpp index 611ea4253d0..a60b06ea0a4 100644 --- a/src/plugins/qmldesigner/designercore/model/model.cpp +++ b/src/plugins/qmldesigner/designercore/model/model.cpp @@ -681,7 +681,6 @@ void ModelPrivate::notifyInstancesChildrenChanged(const QVector &mode void ModelPrivate::notifyCurrentStateChanged(const ModelNode &node) { - m_currentStateNode = node.internalNode(); notifyNodeInstanceViewLast([&](AbstractView *view) { view->currentStateChanged(ModelNode(node.internalNode(), m_model, view)); }); @@ -689,7 +688,6 @@ void ModelPrivate::notifyCurrentStateChanged(const ModelNode &node) void ModelPrivate::notifyCurrentTimelineChanged(const ModelNode &node) { - m_currentTimelineNode = node.internalNode(); notifyNodeInstanceViewLast([&](AbstractView *view) { view->currentTimelineChanged(ModelNode(node.internalNode(), m_model, view)); }); @@ -1913,6 +1911,26 @@ void Model::endDrag() d->notifyDragEnded(); } +void Model::setCurrentStateNode(const ModelNode &node) +{ + Internal::WriteLocker locker(this); + d->m_currentStateNode = node.internalNode(); + d->notifyCurrentStateChanged(node); +} + +// QTC_TEMP +ModelNode Model::currentStateNode(AbstractView *view) +{ + return ModelNode(d->currentStateNode(), this, view); +} + +void Model::setCurrentTimeline(const ModelNode &timeline) +{ + d->m_currentTimelineNode = timeline.internalNode(); + + d->notifyCurrentTimelineChanged(timeline); +} + NotNullPointer Model::projectStorage() const { return d->projectStorage; From 3586a5b4593274d12098d22daa461bd578dfc969 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 15 Feb 2024 13:28:37 +0100 Subject: [PATCH 072/176] QmlDesigner: More tracers Task-number: QDS-11952 Change-Id: I9dc7325b73f85ed2e401689d255acd0ef68b094c Reviewed-by: Tim Jenssen Reviewed-by: Qt CI Patch Build Bot --- src/libs/nanotrace/nanotracehr.h | 22 +++++++++++ .../designercore/model/abstractview.cpp | 3 ++ .../model/internalnodelistproperty.cpp | 2 + .../qmldesigner/designercore/model/model.cpp | 38 ++++++++++++++++++- .../qmldesigner/designercore/model/model_p.h | 4 +- 5 files changed, 66 insertions(+), 3 deletions(-) diff --git a/src/libs/nanotrace/nanotracehr.h b/src/libs/nanotrace/nanotracehr.h index 1a244bd5f44..7c83418a34f 100644 --- a/src/libs/nanotrace/nanotracehr.h +++ b/src/libs/nanotrace/nanotracehr.h @@ -749,6 +749,12 @@ public: return {}; } + template + [[nodiscard]] AsynchronousToken begin(const FlowTokenType &, ArgumentType, Arguments &&...) + { + return {}; + } + template [[nodiscard]] std::pair beginWithFlow(ArgumentType, Arguments &&...) @@ -844,6 +850,22 @@ public: return AsynchronousToken{std::move(name), m_id, m_category}; } + template + [[nodiscard]] AsynchronousToken begin(const FlowTokenType &flowToken, + ArgumentType name, + Arguments &&...arguments) + { + if (m_id) + m_category().begin('b', + m_id, + name, + flowToken.bindId(), + IsFlow::In, + std::forward(arguments)...); + + return AsynchronousToken{std::move(name), m_id, m_category}; + } + template [[nodiscard]] std::pair beginWithFlow(ArgumentType name, Arguments &&...arguments) diff --git a/src/plugins/qmldesigner/designercore/model/abstractview.cpp b/src/plugins/qmldesigner/designercore/model/abstractview.cpp index 32bf050a2fa..35db365a900 100644 --- a/src/plugins/qmldesigner/designercore/model/abstractview.cpp +++ b/src/plugins/qmldesigner/designercore/model/abstractview.cpp @@ -25,6 +25,9 @@ namespace QmlDesigner { +using namespace NanotraceHR::Literals; +using NanotraceHR::keyValue; + /*! \class QmlDesigner::AbstractView \ingroup CoreModel diff --git a/src/plugins/qmldesigner/designercore/model/internalnodelistproperty.cpp b/src/plugins/qmldesigner/designercore/model/internalnodelistproperty.cpp index c0d3944b066..975927c097c 100644 --- a/src/plugins/qmldesigner/designercore/model/internalnodelistproperty.cpp +++ b/src/plugins/qmldesigner/designercore/model/internalnodelistproperty.cpp @@ -64,6 +64,8 @@ const QList &InternalNodeListProperty::nodeList() const void InternalNodeListProperty::slide(int from, int to) { + traceToken.tick("slide"_t, keyValue("from", from), keyValue("to", to)); + InternalNode::Pointer internalNode = m_nodeList.takeAt(from); m_nodeList.insert(to, internalNode); } diff --git a/src/plugins/qmldesigner/designercore/model/model.cpp b/src/plugins/qmldesigner/designercore/model/model.cpp index a60b06ea0a4..d817cbaa579 100644 --- a/src/plugins/qmldesigner/designercore/model/model.cpp +++ b/src/plugins/qmldesigner/designercore/model/model.cpp @@ -64,6 +64,9 @@ Components that want to be informed about changes in the model can register a su */ namespace QmlDesigner { + +using NanotraceHR::keyValue; + namespace Internal { ModelPrivate::ModelPrivate(Model *model, @@ -143,6 +146,8 @@ ModelPrivate::~ModelPrivate() void ModelPrivate::detachAllViews() { + auto tracer = traceToken.begin("detach all views"_t); + for (const QPointer &view : std::as_const(m_viewList)) detachView(view.data(), true); @@ -176,6 +181,8 @@ Storage::Imports createStorageImports(const Imports &imports, void ModelPrivate::changeImports(Imports toBeAddedImports, Imports toBeRemovedImports) { + auto tracer = traceToken.begin("change imports"_t); + std::sort(toBeAddedImports.begin(), toBeAddedImports.end()); std::sort(toBeRemovedImports.begin(), toBeRemovedImports.end()); @@ -251,6 +258,8 @@ void ModelPrivate::setDocumentMessages(const QList &errors, void ModelPrivate::setFileUrl(const QUrl &fileUrl) { + auto tracer = traceToken.begin("file url"_t); + QUrl oldPath = m_fileUrl; if (oldPath != fileUrl) { @@ -299,7 +308,7 @@ InternalNodePointer ModelPrivate::createNode(const TypeName &typeName, majorVersion, minorVersion, internalId, - m_traceToken.tickWithFlow("create node"_t)); + traceToken.tickWithFlow("create node"_t)); setTypeId(newNode.get(), typeName); @@ -1095,6 +1104,8 @@ void ModelPrivate::notifyNodeOrderChanged(const InternalNodeListProperty *intern void ModelPrivate::setSelectedNodes(const QList &selectedNodeList) { + auto tracer = traceToken.begin("selected model nodes"_t); + auto sortedSelectedList = Utils::filtered(selectedNodeList, [](const auto &node) { return node && node->isValid; }); @@ -1106,6 +1117,11 @@ void ModelPrivate::setSelectedNodes(const QList &selectedNo if (sortedSelectedList == m_selectedInternalNodeList) return; + for (auto &node : sortedSelectedList) { + auto flowToken = node->traceToken.tickWithFlow("select model nodes"_t); + traceToken.tick(flowToken, "select model node"_t); + } + const QList lastSelectedNodeList = m_selectedInternalNodeList; m_selectedInternalNodeList = sortedSelectedList; @@ -1114,6 +1130,8 @@ void ModelPrivate::setSelectedNodes(const QList &selectedNo void ModelPrivate::clearSelectedNodes() { + auto tracer = traceToken.begin("clear selected model nodes"_t); + const QList lastSelectedNodeList = m_selectedInternalNodeList; m_selectedInternalNodeList.clear(); changeSelectedNodes(m_selectedInternalNodeList, lastSelectedNodeList); @@ -1490,6 +1508,8 @@ void ModelPrivate::changeRootNodeType(const TypeName &type, int majorVersion, in { Q_ASSERT(rootNode()); + m_rootInternalNode->traceToken.tick("type name"_t, keyValue("type name", type)); + m_rootInternalNode->typeName = type; m_rootInternalNode->majorVersion = majorVersion; m_rootInternalNode->minorVersion = minorVersion; @@ -1499,6 +1519,8 @@ void ModelPrivate::changeRootNodeType(const TypeName &type, int majorVersion, in void ModelPrivate::setScriptFunctions(const InternalNodePointer &node, const QStringList &scriptFunctionList) { + m_rootInternalNode->traceToken.tick("script function"_t); + node->scriptFunctions = scriptFunctionList; notifyScriptFunctionsChanged(node, scriptFunctionList); @@ -1506,6 +1528,8 @@ void ModelPrivate::setScriptFunctions(const InternalNodePointer &node, const QSt void ModelPrivate::setNodeSource(const InternalNodePointer &node, const QString &nodeSource) { + m_rootInternalNode->traceToken.tick("node source"_t); + node->nodeSource = nodeSource; notifyNodeSourceChanged(node, nodeSource); } @@ -1721,6 +1745,8 @@ void Model::changeImports(Imports importsToBeAdded, Imports importsToBeRemoved) void Model::setPossibleImports(Imports possibleImports) { + auto tracer = d->traceToken.begin("possible imports"_t); + std::sort(possibleImports.begin(), possibleImports.end()); if (d->m_possibleImportList != possibleImports) { @@ -1731,6 +1757,8 @@ void Model::setPossibleImports(Imports possibleImports) void Model::setUsedImports(Imports usedImports) { + auto tracer = d->traceToken.begin("used imports"_t); + std::sort(usedImports.begin(), usedImports.end()); if (d->m_usedImportList != usedImports) { @@ -2594,6 +2622,10 @@ The view is informed that it has been registered within the model by a call to A */ void Model::attachView(AbstractView *view) { + auto traceToken = d->traceToken.begin("attachView"_t, + keyValue("name", + std::string_view{view->metaObject()->className()})); + // Internal::WriteLocker locker(d); auto castedRewriterView = qobject_cast(view); if (castedRewriterView) { @@ -2621,6 +2653,10 @@ void Model::attachView(AbstractView *view) */ void Model::detachView(AbstractView *view, ViewNotification emitDetachNotify) { + auto traceToken = d->traceToken.begin("detachView"_t, + keyValue("name", + std::string_view{view->metaObject()->className()})); + // Internal::WriteLocker locker(d); bool emitNotify = (emitDetachNotify == NotifyView); diff --git a/src/plugins/qmldesigner/designercore/model/model_p.h b/src/plugins/qmldesigner/designercore/model/model_p.h index 07aff386bb6..a3e972f329f 100644 --- a/src/plugins/qmldesigner/designercore/model/model_p.h +++ b/src/plugins/qmldesigner/designercore/model/model_p.h @@ -333,6 +333,7 @@ private: public: NotNullPointer projectStorage = nullptr; NotNullPointer pathCache = nullptr; + ModelTracing::AsynchronousToken traceToken = ModelTracing::category().beginAsynchronous("Model"_t); private: Model *m_model = nullptr; @@ -357,8 +358,7 @@ private: QPointer m_nodeInstanceView; QPointer m_metaInfoProxyModel; QHash> m_nodeMetaInfoCache; - ModelTracing::AsynchronousToken m_traceToken = ModelTracing::category().beginAsynchronous( - "Model"_t); + bool m_writeLock = false; qint32 m_internalIdCounter = 1; }; From fe77ba28f83a051b4b38abfaa11993f80e401f48 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Mon, 19 Feb 2024 15:57:08 +0100 Subject: [PATCH 073/176] QmlDesigner: Move component specific code out of the model The model is an abstraction for communication between components and should not used as place for component specific code. Otherwise it will grow quite dramatically. Add Utils3D in component core which is shared between all components is actually the place to share code between components. Change-Id: Ic9d0be72e4480fc33ac6300a10871db4983b4a73 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen Reviewed-by: Miikka Heikkinen --- src/plugins/qmldesigner/CMakeLists.txt | 1 + .../assetslibrary/assetslibrarywidget.cpp | 16 +++++---- .../components/componentcore/utils3d.cpp | 34 +++++++++++++++++++ .../components/componentcore/utils3d.h | 18 ++++++++++ .../contentlibrary/contentlibraryview.cpp | 17 +++++++--- .../contentlibrary/contentlibraryview.h | 5 ++- .../qmldesigner/components/createtexture.cpp | 3 +- .../components/edit3d/bakelights.cpp | 3 +- .../components/edit3d/edit3dview.cpp | 13 +++++-- .../components/edit3d/edit3dview.h | 1 + .../components/edit3d/edit3dwidget.cpp | 8 ++--- .../components/integration/designdocument.cpp | 14 ++++---- .../materialbrowser/materialbrowserview.cpp | 11 +++++- .../materialbrowser/materialbrowserview.h | 5 ++- .../designercore/include/abstractview.h | 3 -- .../include/auxiliarydataproperties.h | 2 -- .../qmldesigner/designercore/include/model.h | 3 -- .../designercore/model/abstractview.cpp | 15 -------- .../qmldesigner/designercore/model/model.cpp | 23 ------------- 19 files changed, 119 insertions(+), 76 deletions(-) create mode 100644 src/plugins/qmldesigner/components/componentcore/utils3d.cpp create mode 100644 src/plugins/qmldesigner/components/componentcore/utils3d.h diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index cab1941f7bd..0ca1db57516 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -626,6 +626,7 @@ extend_qtc_plugin(QmlDesigner anchoraction.cpp anchoraction.h svgpasteaction.cpp svgpasteaction.h viewmanager.cpp viewmanager.h + utils3d.cpp utils3d.h ) extend_qtc_plugin(QmlDesigner diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp index 908fe7a15b4..775505c827c 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp @@ -9,11 +9,12 @@ #include "assetslibraryview.h" #include "designeractionmanager.h" #include "import.h" -#include "nodemetainfo.h" #include "modelnodeoperations.h" +#include "nodemetainfo.h" #include "qmldesignerconstants.h" #include "qmldesignerplugin.h" #include "theme.h" +#include #include @@ -229,16 +230,18 @@ int AssetsLibraryWidget::qtVersion() const void AssetsLibraryWidget::addTextures(const QStringList &filePaths) { m_assetsView->executeInTransaction(__FUNCTION__, [&] { - m_createTextures.execute(filePaths, AddTextureMode::Texture, - m_assetsView->model()->active3DSceneId()); + m_createTextures.execute(filePaths, + AddTextureMode::Texture, + Utils3D::active3DSceneId(m_assetsView->model())); }); } void AssetsLibraryWidget::addLightProbe(const QString &filePath) { m_assetsView->executeInTransaction(__FUNCTION__, [&] { - m_createTextures.execute({filePath}, AddTextureMode::LightProbe, - m_assetsView->model()->active3DSceneId()); + m_createTextures.execute({filePath}, + AddTextureMode::LightProbe, + Utils3D::active3DSceneId(m_assetsView->model())); }); } @@ -247,7 +250,8 @@ void AssetsLibraryWidget::updateContextMenuActionsEnableState() setHasMaterialLibrary(m_assetsView->materialLibraryNode().isValid() && m_assetsView->model()->hasImport("QtQuick3D")); - ModelNode activeSceneEnv = m_createTextures.resolveSceneEnv(m_assetsView->model()->active3DSceneId()); + ModelNode activeSceneEnv = m_createTextures.resolveSceneEnv( + Utils3D::active3DSceneId(m_assetsView->model())); setHasSceneEnv(activeSceneEnv.isValid()); } diff --git a/src/plugins/qmldesigner/components/componentcore/utils3d.cpp b/src/plugins/qmldesigner/components/componentcore/utils3d.cpp new file mode 100644 index 00000000000..c7653c971f3 --- /dev/null +++ b/src/plugins/qmldesigner/components/componentcore/utils3d.cpp @@ -0,0 +1,34 @@ +// 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 "utils3d.h" + +namespace QmlDesigner { +namespace Utils3D { + +ModelNode active3DSceneNode(AbstractView *view) +{ + if (!view) + return {}; + + auto activeSceneAux = view->rootModelNode().auxiliaryData(active3dSceneProperty); + if (activeSceneAux) { + int activeScene = activeSceneAux->toInt(); + + if (view->hasModelNodeForInternalId(activeScene)) + return view->modelNodeForInternalId(activeScene); + } + + return {}; +} + +qint32 active3DSceneId(Model *model) +{ + auto sceneId = model->rootModelNode().auxiliaryData(active3dSceneProperty); + if (sceneId) + return sceneId->toInt(); + return -1; +} + +} // namespace Utils3D +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/componentcore/utils3d.h b/src/plugins/qmldesigner/components/componentcore/utils3d.h new file mode 100644 index 00000000000..2a56b4fcf82 --- /dev/null +++ b/src/plugins/qmldesigner/components/componentcore/utils3d.h @@ -0,0 +1,18 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +// please put here 3d related functions which have no clear place elsewhere + +#include + +namespace QmlDesigner { +namespace Utils3D { + +inline constexpr AuxiliaryDataKeyView active3dSceneProperty{AuxiliaryDataType::Temporary, + "active3dScene"}; + +ModelNode active3DSceneNode(AbstractView *view); +qint32 active3DSceneId(Model *model); + +} // namespace Utils3D +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp index 81be76ed320..fedec758962 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp @@ -16,6 +16,7 @@ #include "qmldesignerconstants.h" #include "qmlobjectnode.h" #include "variantproperty.h" +#include #include #include @@ -143,7 +144,7 @@ WidgetInfo ContentLibraryView::widgetInfo() QTC_ASSERT(typeName.size(), return); if (!m_bundleEffectTarget) - m_bundleEffectTarget = active3DSceneNode(); + m_bundleEffectTarget = Utils3D::active3DSceneNode(this); QTC_ASSERT(m_bundleEffectTarget, return); @@ -168,7 +169,7 @@ WidgetInfo ContentLibraryView::widgetInfo() QTC_ASSERT(metaInfo.isValid(), return); if (!m_bundleEffectTarget) - m_bundleEffectTarget = active3DSceneNode(); + m_bundleEffectTarget = Utils3D::active3DSceneNode(this); QTC_ASSERT(m_bundleEffectTarget, return); @@ -226,7 +227,7 @@ void ContentLibraryView::modelAttached(Model *model) m_widget->setHasQuick3DImport(m_hasQuick3DImport); m_widget->setIsQt6Project(externalDependencies().isQt6Project()); - m_sceneId = model->active3DSceneId(); + m_sceneId = Utils3D::active3DSceneId(model); m_widget->setHasActive3DScene(m_sceneId != -1); m_widget->clearSearchFilter(); @@ -322,7 +323,7 @@ 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() : active3DSceneNode(); + m_bundleEffectTarget = nodeList.first() ? nodeList.first() : Utils3D::active3DSceneNode(this); } } @@ -341,6 +342,14 @@ void ContentLibraryView::nodeAboutToBeRemoved(const ModelNode &removedNode) m_widget->setHasMaterialLibrary(false); } +void ContentLibraryView::auxiliaryDataChanged(const ModelNode &, + AuxiliaryDataKeyView type, + const QVariant &data) +{ + if (type == Utils3D::active3dSceneProperty) + active3DSceneChanged(data.toInt()); +} + #ifdef QDS_USE_PROJECTSTORAGE void ContentLibraryView::applyBundleMaterialToDropTarget(const ModelNode &bundleMat, const TypeName &typeName) diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h index 36b579bf759..3b57b7a4abb 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h @@ -33,7 +33,6 @@ public: void modelAttached(Model *model) override; void modelAboutToBeDetached(Model *model) override; void importsChanged(const Imports &addedImports, const Imports &removedImports) override; - void active3DSceneChanged(qint32 sceneId) override; void selectedNodesChanged(const QList &selectedNodeList, const QList &lastSelectedNodeList) override; void customNotification(const AbstractView *view, const QString &identifier, @@ -42,8 +41,12 @@ public: const NodeAbstractProperty &oldPropertyParent, AbstractView::PropertyChangeFlags propertyChange) override; void nodeAboutToBeRemoved(const ModelNode &removedNode) override; + void auxiliaryDataChanged(const ModelNode &node, + AuxiliaryDataKeyView type, + const QVariant &data) override; private: + void active3DSceneChanged(qint32 sceneId); void updateBundleMaterialsImportedState(); void updateBundleEffectsImportedState(); void updateBundlesQuick3DVersion(); diff --git a/src/plugins/qmldesigner/components/createtexture.cpp b/src/plugins/qmldesigner/components/createtexture.cpp index 8ef538bd2f8..b48ff6d0f8c 100644 --- a/src/plugins/qmldesigner/components/createtexture.cpp +++ b/src/plugins/qmldesigner/components/createtexture.cpp @@ -12,6 +12,7 @@ #include "nodemetainfo.h" #include "qmlobjectnode.h" #include "variantproperty.h" +#include #include @@ -117,7 +118,7 @@ ModelNode CreateTexture::resolveSceneEnv(int sceneId) if (selectedNode.metaInfo().isQtQuick3DSceneEnvironment()) { activeSceneEnv = selectedNode; } else if (sceneId != -1) { - ModelNode activeScene = m_view->active3DSceneNode(); + ModelNode activeScene = Utils3D::active3DSceneNode(m_view); if (activeScene.isValid()) { QmlObjectNode view3D; if (activeScene.metaInfo().isQtQuick3DView3D()) { diff --git a/src/plugins/qmldesigner/components/edit3d/bakelights.cpp b/src/plugins/qmldesigner/components/edit3d/bakelights.cpp index 35d59f07ef4..4e7c399b1c8 100644 --- a/src/plugins/qmldesigner/components/edit3d/bakelights.cpp +++ b/src/plugins/qmldesigner/components/edit3d/bakelights.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -84,7 +85,7 @@ ModelNode BakeLights::resolveView3dNode(AbstractView *view) return {}; ModelNode activeView3D; - ModelNode activeScene = view->active3DSceneNode(); + ModelNode activeScene = Utils3D::active3DSceneNode(view); if (activeScene.isValid()) { if (activeScene.metaInfo().isQtQuick3DView3D()) { diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp index 12647f014ca..adea6f4b8d6 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp @@ -23,7 +23,9 @@ #include "seekerslider.h" #include "snapconfiguration.h" +#include #include +#include #include #include @@ -143,7 +145,7 @@ void Edit3DView::updateActiveScene3D(const QVariantMap &sceneState) if (sceneState.contains(sceneKey)) { qint32 newActiveScene = sceneState[sceneKey].value(); edit3DWidget()->canvas()->updateActiveScene(newActiveScene); - model()->setActive3DSceneId(newActiveScene); + setActive3DSceneId(newActiveScene); updateAlignActionStates(); } @@ -238,7 +240,7 @@ void Edit3DView::updateActiveScene3D(const QVariantMap &sceneState) bool desiredSyncValue = false; if (sceneState.contains(syncEnvBgKey)) desiredSyncValue = sceneState[syncEnvBgKey].toBool(); - ModelNode checkNode = active3DSceneNode(); + ModelNode checkNode = Utils3D::active3DSceneNode(this); const bool activeSceneValid = checkNode.isValid(); while (checkNode.isValid()) { @@ -385,7 +387,7 @@ void Edit3DView::updateAlignActionStates() { bool enabled = false; - ModelNode activeScene = active3DSceneNode(); + ModelNode activeScene = Utils3D::active3DSceneNode(this); if (activeScene.isValid()) { const QList nodes = activeScene.allSubModelNodes(); enabled = ::Utils::anyOf(nodes, [](const ModelNode &node) { @@ -397,6 +399,11 @@ void Edit3DView::updateAlignActionStates() m_alignViewAction->action()->setEnabled(enabled); } +void Edit3DView::setActive3DSceneId(qint32 sceneId) +{ + rootModelNode().setAuxiliaryData(Utils3D::active3dSceneProperty, sceneId); +} + void Edit3DView::modelAboutToBeDetached(Model *model) { m_isBakingLightsSupported = false; diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.h b/src/plugins/qmldesigner/components/edit3d/edit3dview.h index 56248d9587c..5fc0d2961c9 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.h +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.h @@ -111,6 +111,7 @@ private: void handleEntriesChanged(); void showMaterialPropertiesView(); void updateAlignActionStates(); + void setActive3DSceneId(qint32 sceneId); void createSelectBackgroundColorAction(QAction *syncEnvBackgroundAction); void createGridColorSelectionAction(); diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp index ff5852c6312..dc57d75f7a6 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp @@ -19,6 +19,7 @@ #include "qmleditormenu.h" #include "qmlvisualnode.h" #include "viewmanager.h" +#include #include #include @@ -479,10 +480,7 @@ void Edit3DWidget::onCreateAction(QAction *action) if (!m_view->model()->hasImport(import, true, true)) m_view->model()->changeImports({import}, {}); - int activeScene = -1; - auto data = m_view->rootModelNode().auxiliaryData(active3dSceneProperty); - if (data) - activeScene = data->toInt(); + int activeScene = Utils3D::active3DSceneId(m_view->model()); auto modelNode = QmlVisualNode::createQml3DNode(m_view, entry, activeScene, m_contextMenuPos3d).modelNode(); QTC_ASSERT(modelNode.isValid(), return); @@ -688,7 +686,7 @@ void Edit3DWidget::dragEnterEvent(QDragEnterEvent *dragEnterEvent) || dragEnterEvent->mimeData()->hasFormat(Constants::MIME_TYPE_BUNDLE_MATERIAL) || dragEnterEvent->mimeData()->hasFormat(Constants::MIME_TYPE_BUNDLE_EFFECT) || dragEnterEvent->mimeData()->hasFormat(Constants::MIME_TYPE_TEXTURE)) { - if (m_view->active3DSceneNode().isValid()) + if (Utils3D::active3DSceneNode(m_view).isValid()) dragEnterEvent->acceptProposedAction(); } else if (dragEnterEvent->mimeData()->hasFormat(Constants::MIME_TYPE_ITEM_LIBRARY_INFO)) { QByteArray data = dragEnterEvent->mimeData()->data(Constants::MIME_TYPE_ITEM_LIBRARY_INFO); diff --git a/src/plugins/qmldesigner/components/integration/designdocument.cpp b/src/plugins/qmldesigner/components/integration/designdocument.cpp index 7b7f4a51ef0..a82f5744efa 100644 --- a/src/plugins/qmldesigner/components/integration/designdocument.cpp +++ b/src/plugins/qmldesigner/components/integration/designdocument.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -232,13 +233,12 @@ void DesignDocument::moveNodesToPosition(const QList &nodes, const st }); if (all3DNodes) { - auto data = rootModelNode().auxiliaryData(active3dSceneProperty); - if (data) { - if (int activeSceneId = data->toInt(); activeSceneId != -1) { - NodeListProperty sceneNodeProperty = QmlVisualNode::findSceneNodeProperty( - rootModelNode().view(), activeSceneId); - targetNode = sceneNodeProperty.parentModelNode(); - } + int activeSceneId = Utils3D::active3DSceneId(m_documentModel.get()); + + if (activeSceneId != -1) { + NodeListProperty sceneNodeProperty = QmlVisualNode::findSceneNodeProperty( + rootModelNode().view(), activeSceneId); + targetNode = sceneNodeProperty.parentModelNode(); } } } diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp index 76ae09a453e..8bd57617285 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp @@ -16,6 +16,7 @@ #include "qmldesignerconstants.h" #include "qmlobjectnode.h" #include "variantproperty.h" +#include #include @@ -240,7 +241,7 @@ void MaterialBrowserView::modelAttached(Model *model) loadPropertyGroups(); // Needs the delay because it uses metaInfo }); - m_sceneId = model->active3DSceneId(); + m_sceneId = Utils3D::active3DSceneId(model); } void MaterialBrowserView::refreshModel(bool updateImages) @@ -585,6 +586,14 @@ void MaterialBrowserView::instancePropertyChanged(const QList &nodeList, const QList &data) override; void instancesCompleted(const QVector &completedNodeList) override; void instancePropertyChanged(const QList > &propertyList) override; - void active3DSceneChanged(qint32 sceneId) override; + void auxiliaryDataChanged(const ModelNode &node, + AuxiliaryDataKeyView type, + const QVariant &data) override; void currentStateChanged(const ModelNode &node) override; void applyTextureToModel3D(const QmlObjectNode &model3D, const ModelNode &texture = {}); @@ -65,6 +67,7 @@ protected: bool eventFilter(QObject *obj, QEvent *event) override; private: + void active3DSceneChanged(qint32 sceneId); void refreshModel(bool updateImages); void updateMaterialsPreview(); bool isMaterial(const ModelNode &node) const; diff --git a/src/plugins/qmldesigner/designercore/include/abstractview.h b/src/plugins/qmldesigner/designercore/include/abstractview.h index 71c675fd3cb..4f1ddbd5700 100644 --- a/src/plugins/qmldesigner/designercore/include/abstractview.h +++ b/src/plugins/qmldesigner/designercore/include/abstractview.h @@ -223,8 +223,6 @@ public: virtual void view3DAction(View3DActionType type, const QVariant &value); - virtual void active3DSceneChanged(qint32 sceneId); - virtual void dragStarted(QMimeData *mimeData); virtual void dragEnded(); @@ -233,7 +231,6 @@ public: void ensureMaterialLibraryNode(); ModelNode materialLibraryNode(); bool isPartOfMaterialLibrary(const ModelNode &node); - ModelNode active3DSceneNode(); ModelNode getTextureDefaultInstance(const QString &source); const NodeInstanceView *nodeInstanceView() const; diff --git a/src/plugins/qmldesigner/designercore/include/auxiliarydataproperties.h b/src/plugins/qmldesigner/designercore/include/auxiliarydataproperties.h index fe55402f174..b6bb22290a7 100644 --- a/src/plugins/qmldesigner/designercore/include/auxiliarydataproperties.h +++ b/src/plugins/qmldesigner/designercore/include/auxiliarydataproperties.h @@ -95,8 +95,6 @@ inline constexpr AuxiliaryDataKeyDefaultValue insightCategoriesProperty{Auxiliar "insightCategories", {}}; inline constexpr AuxiliaryDataKeyView uuidProperty{AuxiliaryDataType::Document, "uuid"}; -inline constexpr AuxiliaryDataKeyView active3dSceneProperty{AuxiliaryDataType::Temporary, - "active3dScene"}; inline constexpr AuxiliaryDataKeyView tmpProperty{AuxiliaryDataType::Temporary, "tmp"}; inline constexpr AuxiliaryDataKeyView recordProperty{AuxiliaryDataType::Temporary, "Record"}; inline constexpr AuxiliaryDataKeyView transitionDurationProperty{AuxiliaryDataType::Document, diff --git a/src/plugins/qmldesigner/designercore/include/model.h b/src/plugins/qmldesigner/designercore/include/model.h index c6e1bea1120..67179efede3 100644 --- a/src/plugins/qmldesigner/designercore/include/model.h +++ b/src/plugins/qmldesigner/designercore/include/model.h @@ -239,9 +239,6 @@ public: std::optional> isDuplicate = {}) const; QString generateIdFromName(const QString &name, const QString &fallbackId = "element") const; - void setActive3DSceneId(qint32 sceneId); - qint32 active3DSceneId() const; - void startDrag(QMimeData *mimeData, const QPixmap &icon); void endDrag(); diff --git a/src/plugins/qmldesigner/designercore/model/abstractview.cpp b/src/plugins/qmldesigner/designercore/model/abstractview.cpp index 35db365a900..37fd6fe8606 100644 --- a/src/plugins/qmldesigner/designercore/model/abstractview.cpp +++ b/src/plugins/qmldesigner/designercore/model/abstractview.cpp @@ -373,8 +373,6 @@ void AbstractView::modelNodePreviewPixmapChanged(const ModelNode &/*node*/, cons void AbstractView::view3DAction(View3DActionType, const QVariant &) {} -void AbstractView::active3DSceneChanged(qint32 /*sceneId*/) {} - void AbstractView::dragStarted(QMimeData * /*mimeData*/) {} void AbstractView::dragEnded() {} @@ -837,19 +835,6 @@ bool AbstractView::isPartOfMaterialLibrary(const ModelNode &node) || (node.hasParentProperty() && node.parentProperty().parentModelNode() == matLib)); } -ModelNode AbstractView::active3DSceneNode() -{ - auto activeSceneAux = rootModelNode().auxiliaryData(active3dSceneProperty); - if (activeSceneAux) { - int activeScene = activeSceneAux->toInt(); - - if (hasModelNodeForInternalId(activeScene)) - return modelNodeForInternalId(activeScene); - } - - return {}; -} - ModelNode AbstractView::getTextureDefaultInstance(const QString &source) { ModelNode matLib = materialLibraryNode(); diff --git a/src/plugins/qmldesigner/designercore/model/model.cpp b/src/plugins/qmldesigner/designercore/model/model.cpp index d817cbaa579..61f637e4c56 100644 --- a/src/plugins/qmldesigner/designercore/model/model.cpp +++ b/src/plugins/qmldesigner/designercore/model/model.cpp @@ -733,11 +733,6 @@ void ModelPrivate::notifyView3DAction(View3DActionType type, const QVariant &val notifyNormalViewsLast([&](AbstractView *view) { view->view3DAction(type, value); }); } -void ModelPrivate::notifyActive3DSceneIdChanged(qint32 sceneId) -{ - notifyInstanceChanges([&](AbstractView *view) { view->active3DSceneChanged(sceneId); }); -} - void ModelPrivate::notifyDragStarted(QMimeData *mimeData) { notifyInstanceChanges([&](AbstractView *view) { view->dragStarted(mimeData); }); @@ -1903,24 +1898,6 @@ QString Model::generateIdFromName(const QString &name, const QString &fallbackId return newId; } -void Model::setActive3DSceneId(qint32 sceneId) -{ - auto activeSceneAux = d->rootNode()->auxiliaryData(active3dSceneProperty); - if (activeSceneAux && activeSceneAux->toInt() == sceneId) - return; - - d->rootNode()->setAuxiliaryData(active3dSceneProperty, sceneId); - d->notifyActive3DSceneIdChanged(sceneId); -} - -qint32 Model::active3DSceneId() const -{ - auto sceneId = d->rootNode()->auxiliaryData(active3dSceneProperty); - if (sceneId) - return sceneId->toInt(); - return -1; -} - void Model::startDrag(QMimeData *mimeData, const QPixmap &icon) { d->notifyDragStarted(mimeData); From 483c7afefd954d6e268703fef5d31e774921c203 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Tue, 20 Feb 2024 11:16:51 +0100 Subject: [PATCH 074/176] QmlDesigner: Make global variables inline constexpr Leads to potential less symbols because of weak linking. Change-Id: Iadeab6fa550b895218f78f59fc0956ac0625633f Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen Reviewed-by: --- .../qmldesigner/qmldesignerconstants.h | 313 +++++++++--------- 1 file changed, 161 insertions(+), 152 deletions(-) diff --git a/src/plugins/qmldesigner/qmldesignerconstants.h b/src/plugins/qmldesigner/qmldesignerconstants.h index cd5a8a2b238..547d42e6e99 100644 --- a/src/plugins/qmldesigner/qmldesignerconstants.h +++ b/src/plugins/qmldesigner/qmldesignerconstants.h @@ -6,176 +6,185 @@ namespace QmlDesigner { namespace Constants { -const char C_BACKSPACE[] = "QmlDesigner.Backspace"; -const char C_DELETE[] = "QmlDesigner.Delete"; -const char C_DUPLICATE[] = "QmlDesigner.Duplicate"; +inline constexpr char C_BACKSPACE[] = "QmlDesigner.Backspace"; +inline constexpr char C_DELETE[] = "QmlDesigner.Delete"; +inline constexpr char C_DUPLICATE[] = "QmlDesigner.Duplicate"; // Context -const char C_QMLDESIGNER[] = "QmlDesigner::QmlDesignerMain"; -const char C_QMLFORMEDITOR[] = "QmlDesigner::FormEditor"; -const char C_QMLEDITOR3D[] = "QmlDesigner::Editor3D"; -const char C_QMLEFFECTCOMPOSER[] = "QmlDesigner::EffectComposer"; -const char C_QMLNAVIGATOR[] = "QmlDesigner::Navigator"; -const char C_QMLTEXTEDITOR[] = "QmlDesigner::TextEditor"; -const char C_QMLMATERIALBROWSER[] = "QmlDesigner::MaterialBrowser"; -const char C_QMLASSETSLIBRARY[] = "QmlDesigner::AssetsLibrary"; -const char C_QMLCOLLECTIONEDITOR[] = "QmlDesigner::CollectionEditor"; +inline constexpr char C_QMLDESIGNER[] = "QmlDesigner::QmlDesignerMain"; +inline constexpr char C_QMLFORMEDITOR[] = "QmlDesigner::FormEditor"; +inline constexpr char C_QMLEDITOR3D[] = "QmlDesigner::Editor3D"; +inline constexpr char C_QMLEFFECTCOMPOSER[] = "QmlDesigner::EffectComposer"; +inline constexpr char C_QMLNAVIGATOR[] = "QmlDesigner::Navigator"; +inline constexpr char C_QMLTEXTEDITOR[] = "QmlDesigner::TextEditor"; +inline constexpr char C_QMLMATERIALBROWSER[] = "QmlDesigner::MaterialBrowser"; +inline constexpr char C_QMLASSETSLIBRARY[] = "QmlDesigner::AssetsLibrary"; +inline constexpr char C_QMLCOLLECTIONEDITOR[] = "QmlDesigner::CollectionEditor"; // Special context for preview menu, shared b/w designer and text editor -const char C_QT_QUICK_TOOLS_MENU[] = "QmlDesigner::ToolsMenu"; +inline constexpr char C_QT_QUICK_TOOLS_MENU[] = "QmlDesigner::ToolsMenu"; // Actions -const char SWITCH_TEXT_DESIGN[] = "QmlDesigner.SwitchTextDesign"; -const char RESTORE_DEFAULT_VIEW[] = "QmlDesigner.RestoreDefaultView"; -const char TOGGLE_LEFT_SIDEBAR[] = "QmlDesigner.ToggleLeftSideBar"; -const char TOGGLE_RIGHT_SIDEBAR[] = "QmlDesigner.ToggleRightSideBar"; -const char TOGGLE_STATES_EDITOR[] = "QmlDesigner.ToggleStatesEditor"; -const char GO_INTO_COMPONENT[] = "QmlDesigner.GoIntoComponent"; -const char EXPORT_AS_IMAGE[] = "QmlDesigner.ExportAsImage"; -const char TAKE_SCREENSHOT[] = "QmlDesigner.TakeScreenshot"; -const char FORMEDITOR_REFRESH[] = "QmlDesigner.FormEditor.Refresh"; -const char FORMEDITOR_SNAPPING[] = "QmlDesigner.FormEditor.Snapping"; -const char FORMEDITOR_NO_SNAPPING[] = "QmlDesigner.FormEditor.NoSnapping"; -const char FORMEDITOR_NO_SNAPPING_AND_ANCHORING[] = "QmlDesigner.FormEditor.NoSnappingAndAnchoring"; -const char FORMEDITOR_NO_SHOW_BOUNDING_RECTANGLE[] = "QmlDesigner.FormEditor.ShowBoundingRectangle"; -const char EDIT3D_SELECTION_MODE[] = "QmlDesigner.Editor3D.SelectionModeToggle"; -const char EDIT3D_MOVE_TOOL[] = "QmlDesigner.Editor3D.MoveTool"; -const char EDIT3D_ROTATE_TOOL[] = "QmlDesigner.Editor3D.RotateTool"; -const char EDIT3D_SCALE_TOOL[] = "QmlDesigner.Editor3D.ScaleTool"; -const char EDIT3D_FIT_SELECTED[] = "QmlDesigner.Editor3D.FitSelected"; -const char EDIT3D_ALIGN_CAMERAS[] = "QmlDesigner.Editor3D.AlignCameras"; -const char EDIT3D_ALIGN_VIEW[] = "QmlDesigner.Editor3D.AlignView"; -const char EDIT3D_EDIT_CAMERA[] = "QmlDesigner.Editor3D.EditCameraToggle"; -const char EDIT3D_ORIENTATION[] = "QmlDesigner.Editor3D.OrientationToggle"; -const char EDIT3D_EDIT_LIGHT[] = "QmlDesigner.Editor3D.EditLightToggle"; -const char EDIT3D_EDIT_SHOW_GRID[] = "QmlDesigner.Editor3D.ToggleGrid"; -const char EDIT3D_EDIT_SELECT_BACKGROUND_COLOR[] = "QmlDesigner.Editor3D.SelectBackgroundColor"; -const char EDIT3D_EDIT_SELECT_GRID_COLOR[] = "QmlDesigner.Editor3D.SelectGridColor"; -const char EDIT3D_EDIT_RESET_BACKGROUND_COLOR[] = "QmlDesigner.Editor3D.ResetBackgroundColor"; -const char EDIT3D_EDIT_SYNC_ENV_BACKGROUND[] = "QmlDesigner.Editor3D.SyncEnvBackground"; -const char EDIT3D_EDIT_SHOW_SELECTION_BOX[] = "QmlDesigner.Editor3D.ToggleSelectionBox"; -const char EDIT3D_EDIT_SHOW_ICON_GIZMO[] = "QmlDesigner.Editor3D.ToggleIconGizmo"; -const char EDIT3D_EDIT_SHOW_CAMERA_FRUSTUM[] = "QmlDesigner.Editor3D.ToggleCameraFrustum"; -const char EDIT3D_EDIT_SHOW_PARTICLE_EMITTER[] = "QmlDesigner.Editor3D.ToggleParticleEmitter"; -const char EDIT3D_RESET_VIEW[] = "QmlDesigner.Editor3D.ResetView"; -const char EDIT3D_PARTICLE_MODE[] = "QmlDesigner.Editor3D.ParticleViewModeToggle"; -const char EDIT3D_PARTICLES_PLAY[] = "QmlDesigner.Editor3D.ParticlesPlay"; -const char EDIT3D_PARTICLES_SEEKER[] = "QmlDesigner.Editor3D.ParticlesSeeker"; -const char EDIT3D_PARTICLES_RESTART[] = "QmlDesigner.Editor3D.ParticlesRestart"; -const char EDIT3D_SPLIT_VIEW[] = "QmlDesigner.Editor3D.SplitViewToggle"; -const char EDIT3D_VISIBILITY_TOGGLES[] = "QmlDesigner.Editor3D.VisibilityToggles"; -const char EDIT3D_BACKGROUND_COLOR_ACTIONS[] = "QmlDesigner.Editor3D.BackgroundColorActions"; -const char EDIT3D_BAKE_LIGHTS[] = "QmlDesigner.Editor3D.BakeLights"; -const char EDIT3D_SNAP_TOGGLE[] = "QmlDesigner.Editor3D.SnapToggle"; -const char EDIT3D_SNAP_CONFIG[] = "QmlDesigner.Editor3D.SnapConfig"; +inline constexpr char SWITCH_TEXT_DESIGN[] = "QmlDesigner.SwitchTextDesign"; +inline constexpr char RESTORE_DEFAULT_VIEW[] = "QmlDesigner.RestoreDefaultView"; +inline constexpr char TOGGLE_LEFT_SIDEBAR[] = "QmlDesigner.ToggleLeftSideBar"; +inline constexpr char TOGGLE_RIGHT_SIDEBAR[] = "QmlDesigner.ToggleRightSideBar"; +inline constexpr char TOGGLE_STATES_EDITOR[] = "QmlDesigner.ToggleStatesEditor"; +inline constexpr char GO_INTO_COMPONENT[] = "QmlDesigner.GoIntoComponent"; +inline constexpr char EXPORT_AS_IMAGE[] = "QmlDesigner.ExportAsImage"; +inline constexpr char TAKE_SCREENSHOT[] = "QmlDesigner.TakeScreenshot"; +inline constexpr char FORMEDITOR_REFRESH[] = "QmlDesigner.FormEditor.Refresh"; +inline constexpr char FORMEDITOR_SNAPPING[] = "QmlDesigner.FormEditor.Snapping"; +inline constexpr char FORMEDITOR_NO_SNAPPING[] = "QmlDesigner.FormEditor.NoSnapping"; +inline constexpr char FORMEDITOR_NO_SNAPPING_AND_ANCHORING[] + = "QmlDesigner.FormEditor.NoSnappingAndAnchoring"; +inline constexpr char FORMEDITOR_NO_SHOW_BOUNDING_RECTANGLE[] + = "QmlDesigner.FormEditor.ShowBoundingRectangle"; +inline constexpr char EDIT3D_SELECTION_MODE[] = "QmlDesigner.Editor3D.SelectionModeToggle"; +inline constexpr char EDIT3D_MOVE_TOOL[] = "QmlDesigner.Editor3D.MoveTool"; +inline constexpr char EDIT3D_ROTATE_TOOL[] = "QmlDesigner.Editor3D.RotateTool"; +inline constexpr char EDIT3D_SCALE_TOOL[] = "QmlDesigner.Editor3D.ScaleTool"; +inline constexpr char EDIT3D_FIT_SELECTED[] = "QmlDesigner.Editor3D.FitSelected"; +inline constexpr char EDIT3D_ALIGN_CAMERAS[] = "QmlDesigner.Editor3D.AlignCameras"; +inline constexpr char EDIT3D_ALIGN_VIEW[] = "QmlDesigner.Editor3D.AlignView"; +inline constexpr char EDIT3D_EDIT_CAMERA[] = "QmlDesigner.Editor3D.EditCameraToggle"; +inline constexpr char EDIT3D_ORIENTATION[] = "QmlDesigner.Editor3D.OrientationToggle"; +inline constexpr char EDIT3D_EDIT_LIGHT[] = "QmlDesigner.Editor3D.EditLightToggle"; +inline constexpr char EDIT3D_EDIT_SHOW_GRID[] = "QmlDesigner.Editor3D.ToggleGrid"; +inline constexpr char EDIT3D_EDIT_SELECT_BACKGROUND_COLOR[] + = "QmlDesigner.Editor3D.SelectBackgroundColor"; +inline constexpr char EDIT3D_EDIT_SELECT_GRID_COLOR[] = "QmlDesigner.Editor3D.SelectGridColor"; +inline constexpr char EDIT3D_EDIT_RESET_BACKGROUND_COLOR[] + = "QmlDesigner.Editor3D.ResetBackgroundColor"; +inline constexpr char EDIT3D_EDIT_SYNC_ENV_BACKGROUND[] = "QmlDesigner.Editor3D.SyncEnvBackground"; +inline constexpr char EDIT3D_EDIT_SHOW_SELECTION_BOX[] = "QmlDesigner.Editor3D.ToggleSelectionBox"; +inline constexpr char EDIT3D_EDIT_SHOW_ICON_GIZMO[] = "QmlDesigner.Editor3D.ToggleIconGizmo"; +inline constexpr char EDIT3D_EDIT_SHOW_CAMERA_FRUSTUM[] + = "QmlDesigner.Editor3D.ToggleCameraFrustum"; +inline constexpr char EDIT3D_EDIT_SHOW_PARTICLE_EMITTER[] + = "QmlDesigner.Editor3D.ToggleParticleEmitter"; +inline constexpr char EDIT3D_RESET_VIEW[] = "QmlDesigner.Editor3D.ResetView"; +inline constexpr char EDIT3D_PARTICLE_MODE[] = "QmlDesigner.Editor3D.ParticleViewModeToggle"; +inline constexpr char EDIT3D_PARTICLES_PLAY[] = "QmlDesigner.Editor3D.ParticlesPlay"; +inline constexpr char EDIT3D_PARTICLES_SEEKER[] = "QmlDesigner.Editor3D.ParticlesSeeker"; +inline constexpr char EDIT3D_PARTICLES_RESTART[] = "QmlDesigner.Editor3D.ParticlesRestart"; +inline constexpr char EDIT3D_SPLIT_VIEW[] = "QmlDesigner.Editor3D.SplitViewToggle"; +inline constexpr char EDIT3D_VISIBILITY_TOGGLES[] = "QmlDesigner.Editor3D.VisibilityToggles"; +inline constexpr char EDIT3D_BACKGROUND_COLOR_ACTIONS[] + = "QmlDesigner.Editor3D.BackgroundColorActions"; +inline constexpr char EDIT3D_BAKE_LIGHTS[] = "QmlDesigner.Editor3D.BakeLights"; +inline constexpr char EDIT3D_SNAP_TOGGLE[] = "QmlDesigner.Editor3D.SnapToggle"; +inline constexpr char EDIT3D_SNAP_CONFIG[] = "QmlDesigner.Editor3D.SnapConfig"; -const char QML_DESIGNER_SUBFOLDER[] = "/designer/"; -const char COMPONENT_BUNDLES_FOLDER[] = "/ComponentBundles"; -const char COMPONENT_BUNDLES_ASSET_REF_FILE[] = "_asset_ref.json"; -const char QUICK_3D_ASSETS_FOLDER[] = "/Quick3DAssets"; -const char QUICK_3D_ASSET_LIBRARY_ICON_SUFFIX[] = "_libicon"; -const char QUICK_3D_ASSET_IMPORT_DATA_NAME[] = "_importdata.json"; -const char QUICK_3D_ASSET_IMPORT_DATA_OPTIONS_KEY[] = "import_options"; -const char QUICK_3D_ASSET_IMPORT_DATA_SOURCE_KEY[] = "source_scene"; -const char DEFAULT_ASSET_IMPORT_FOLDER[] = "/asset_imports"; -const char DEFAULT_EFFECTS_IMPORT_FOLDER[] = "/asset_imports/Effects"; -const char MATERIAL_LIB_ID[] = "__materialLibrary__"; +inline constexpr char QML_DESIGNER_SUBFOLDER[] = "/designer/"; +inline constexpr char COMPONENT_BUNDLES_FOLDER[] = "/ComponentBundles"; +inline constexpr char COMPONENT_BUNDLES_ASSET_REF_FILE[] = "_asset_ref.json"; +inline constexpr char QUICK_3D_ASSETS_FOLDER[] = "/Quick3DAssets"; +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 MATERIAL_LIB_ID[] = "__materialLibrary__"; -const char MIME_TYPE_ITEM_LIBRARY_INFO[] = "application/vnd.qtdesignstudio.itemlibraryinfo"; -const char MIME_TYPE_ASSETS[] = "application/vnd.qtdesignstudio.assets"; -const char MIME_TYPE_MATERIAL[] = "application/vnd.qtdesignstudio.material"; -const char MIME_TYPE_TEXTURE[] = "application/vnd.qtdesignstudio.texture"; -const char MIME_TYPE_BUNDLE_EFFECT[] = "application/vnd.qtdesignstudio.bundleeffect"; -const char MIME_TYPE_BUNDLE_MATERIAL[] = "application/vnd.qtdesignstudio.bundlematerial"; -const char MIME_TYPE_BUNDLE_TEXTURE[] = "application/vnd.qtdesignstudio.bundletexture"; -const char MIME_TYPE_ASSET_IMAGE[] = "application/vnd.qtdesignstudio.asset.image"; -const char MIME_TYPE_ASSET_FONT[] = "application/vnd.qtdesignstudio.asset.font"; -const char MIME_TYPE_ASSET_SHADER[] = "application/vnd.qtdesignstudio.asset.shader"; -const char MIME_TYPE_ASSET_SOUND[] = "application/vnd.qtdesignstudio.asset.sound"; -const char MIME_TYPE_ASSET_VIDEO[] = "application/vnd.qtdesignstudio.asset.video"; -const char MIME_TYPE_ASSET_TEXTURE3D[] = "application/vnd.qtdesignstudio.asset.texture3d"; -const char MIME_TYPE_MODELNODE_LIST[] = "application/vnd.qtdesignstudio.modelnode.list"; -const char MIME_TYPE_ASSET_EFFECT[] = "application/vnd.qtdesignstudio.asset.effect"; +inline constexpr char MIME_TYPE_ITEM_LIBRARY_INFO[] + = "application/vnd.qtdesignstudio.itemlibraryinfo"; +inline constexpr char MIME_TYPE_ASSETS[] = "application/vnd.qtdesignstudio.assets"; +inline constexpr char MIME_TYPE_MATERIAL[] = "application/vnd.qtdesignstudio.material"; +inline constexpr char MIME_TYPE_TEXTURE[] = "application/vnd.qtdesignstudio.texture"; +inline constexpr char MIME_TYPE_BUNDLE_EFFECT[] = "application/vnd.qtdesignstudio.bundleeffect"; +inline constexpr char MIME_TYPE_BUNDLE_MATERIAL[] = "application/vnd.qtdesignstudio.bundlematerial"; +inline constexpr char MIME_TYPE_BUNDLE_TEXTURE[] = "application/vnd.qtdesignstudio.bundletexture"; +inline constexpr char MIME_TYPE_ASSET_IMAGE[] = "application/vnd.qtdesignstudio.asset.image"; +inline constexpr char MIME_TYPE_ASSET_FONT[] = "application/vnd.qtdesignstudio.asset.font"; +inline constexpr char MIME_TYPE_ASSET_SHADER[] = "application/vnd.qtdesignstudio.asset.shader"; +inline constexpr char MIME_TYPE_ASSET_SOUND[] = "application/vnd.qtdesignstudio.asset.sound"; +inline constexpr char MIME_TYPE_ASSET_VIDEO[] = "application/vnd.qtdesignstudio.asset.video"; +inline constexpr char MIME_TYPE_ASSET_TEXTURE3D[] + = "application/vnd.qtdesignstudio.asset.texture3d"; +inline constexpr char MIME_TYPE_MODELNODE_LIST[] = "application/vnd.qtdesignstudio.modelnode.list"; +inline constexpr char MIME_TYPE_ASSET_EFFECT[] = "application/vnd.qtdesignstudio.asset.effect"; // Menus -const char M_VIEW_WORKSPACES[] = "QmlDesigner.Menu.View.Workspaces"; +inline constexpr char M_VIEW_WORKSPACES[] = "QmlDesigner.Menu.View.Workspaces"; const int MODELNODE_PREVIEW_IMAGE_DIMENSIONS = 150; -const char EVENT_TIMELINE_ADDED[] = "timelineAdded"; -const char EVENT_TRANSITION_ADDED[] = "transitionAdded"; -const char EVENT_STATE_ADDED[] = "stateAdded"; -const char EVENT_STATE_ADDED_AND_CLONED[] = "stateAddedAndCloned"; -const char EVENT_STATE_CLONED[] = "stateCloned"; -const char EVENT_STATE_EXTENDED[] = "stateExtended"; -const char EVENT_CONNECTION_ADDED[] = "connectionAdded"; -const char EVENT_PROPERTY_ADDED[] = "propertyAdded"; -const char EVENT_ANNOTATION_ADDED[] = "annotationAdded"; -const char EVENT_RESOURCE_IMPORTED[] = "resourceImported"; -const char EVENT_ACTION_EXECUTED[] = "actionExecuted"; -const char EVENT_HELP_REQUESTED[] = "helpRequested"; -const char EVENT_IMPORT_ADDED[] = "importAdded:"; -const char EVENT_BINDINGEDITOR_OPENED[] = "bindingEditorOpened"; -const char EVENT_RICHTEXT_OPENED[] = "richtextEditorOpened"; -const char EVENT_FORMEDITOR_TIME[] = "formEditor"; -const char EVENT_3DEDITOR_TIME[] = "3DEditor"; -const char EVENT_TIMELINE_TIME[] = "timeline"; -const char EVENT_TRANSITIONEDITOR_TIME[] = "transitionEditor"; -const char EVENT_CURVEDITOR_TIME[] = "curveEditor"; -const char EVENT_STATESEDITOR_TIME[] = "statesEditor"; -const char EVENT_TEXTEDITOR_TIME[] = "textEditor"; -const char EVENT_TEXTUREEDITOR_TIME[] = "textureEditor"; -const char EVENT_PROPERTYEDITOR_TIME[] = "propertyEditor"; -const char EVENT_ASSETSLIBRARY_TIME[] = "assetsLibrary"; -const char EVENT_EFFECTCOMPOSER_NODE[] = "effectComposerNode"; -const char EVENT_EFFECTCOMPOSER_TIME[] = "effectComposerTime"; -const char EVENT_ITEMLIBRARY_TIME[] = "itemLibrary"; -const char EVENT_TRANSLATIONVIEW_TIME[] = "translationView"; -const char EVENT_NAVIGATORVIEW_TIME[] = "navigatorView"; -const char EVENT_DESIGNMODE_TIME[] = "designMode"; -const char EVENT_MATERIALEDITOR_TIME[] = "materialEditor"; -const char EVENT_MATERIALBROWSER_TIME[] = "materialBrowser"; -const char EVENT_CONTENTLIBRARY_TIME[] = "contentLibrary"; -const char EVENT_INSIGHT_TIME[] = "insight"; -const char EVENT_MODELEDITOR_TIME[] = "modelEditor"; -const char EVENT_TOOLBAR_MODE_CHANGE[] = "ToolBarTriggerModeChange"; -const char EVENT_TOOLBAR_PROJECT_SETTINGS[] = "ToolBarTriggerProjectSettings"; -const char EVENT_TOOLBAR_RUN_PROJECT[] = "ToolBarRunProject"; -const char EVENT_TOOLBAR_GO_FORWARD[] = "ToolBarGoForward"; -const char EVENT_TOOLBAR_GO_BACKWARD[] = "ToolBarGoBackward"; -const char EVENT_TOOLBAR_OPEN_FILE[] = "ToolBarOpenFile"; -const char EVENT_TOOLBAR_CLOSE_DOCUMENT[] = "ToolBarCloseCurrentDocument"; -const char EVENT_TOOLBAR_SHARE_APPLICATION[] = "ToolBarShareApplication"; -const char EVENT_TOOLBAR_SET_CURRENT_WORKSPACE[] = "ToolBarSetCurrentWorkspace"; -const char EVENT_TOOLBAR_EDIT_GLOBAL_ANNOTATION[] = "ToolBarEditGlobalAnnotation"; -const char EVENT_STATUSBAR_SHOW_ZOOM[] = "StatusBarShowZoomMenu"; -const char EVENT_STATUSBAR_SET_STYLE[] = "StatusBarSetCurrentStyle"; +inline constexpr char EVENT_TIMELINE_ADDED[] = "timelineAdded"; +inline constexpr char EVENT_TRANSITION_ADDED[] = "transitionAdded"; +inline constexpr char EVENT_STATE_ADDED[] = "stateAdded"; +inline constexpr char EVENT_STATE_ADDED_AND_CLONED[] = "stateAddedAndCloned"; +inline constexpr char EVENT_STATE_CLONED[] = "stateCloned"; +inline constexpr char EVENT_STATE_EXTENDED[] = "stateExtended"; +inline constexpr char EVENT_CONNECTION_ADDED[] = "connectionAdded"; +inline constexpr char EVENT_PROPERTY_ADDED[] = "propertyAdded"; +inline constexpr char EVENT_ANNOTATION_ADDED[] = "annotationAdded"; +inline constexpr char EVENT_RESOURCE_IMPORTED[] = "resourceImported"; +inline constexpr char EVENT_ACTION_EXECUTED[] = "actionExecuted"; +inline constexpr char EVENT_HELP_REQUESTED[] = "helpRequested"; +inline constexpr char EVENT_IMPORT_ADDED[] = "importAdded:"; +inline constexpr char EVENT_BINDINGEDITOR_OPENED[] = "bindingEditorOpened"; +inline constexpr char EVENT_RICHTEXT_OPENED[] = "richtextEditorOpened"; +inline constexpr char EVENT_FORMEDITOR_TIME[] = "formEditor"; +inline constexpr char EVENT_3DEDITOR_TIME[] = "3DEditor"; +inline constexpr char EVENT_TIMELINE_TIME[] = "timeline"; +inline constexpr char EVENT_TRANSITIONEDITOR_TIME[] = "transitionEditor"; +inline constexpr char EVENT_CURVEDITOR_TIME[] = "curveEditor"; +inline constexpr char EVENT_STATESEDITOR_TIME[] = "statesEditor"; +inline constexpr char EVENT_TEXTEDITOR_TIME[] = "textEditor"; +inline constexpr char EVENT_TEXTUREEDITOR_TIME[] = "textureEditor"; +inline constexpr char EVENT_PROPERTYEDITOR_TIME[] = "propertyEditor"; +inline constexpr char EVENT_ASSETSLIBRARY_TIME[] = "assetsLibrary"; +inline constexpr char EVENT_EFFECTCOMPOSER_NODE[] = "effectComposerNode"; +inline constexpr char EVENT_EFFECTCOMPOSER_TIME[] = "effectComposerTime"; +inline constexpr char EVENT_ITEMLIBRARY_TIME[] = "itemLibrary"; +inline constexpr char EVENT_TRANSLATIONVIEW_TIME[] = "translationView"; +inline constexpr char EVENT_NAVIGATORVIEW_TIME[] = "navigatorView"; +inline constexpr char EVENT_DESIGNMODE_TIME[] = "designMode"; +inline constexpr char EVENT_MATERIALEDITOR_TIME[] = "materialEditor"; +inline constexpr char EVENT_MATERIALBROWSER_TIME[] = "materialBrowser"; +inline constexpr char EVENT_CONTENTLIBRARY_TIME[] = "contentLibrary"; +inline constexpr char EVENT_INSIGHT_TIME[] = "insight"; +inline constexpr char EVENT_MODELEDITOR_TIME[] = "modelEditor"; +inline constexpr char EVENT_TOOLBAR_MODE_CHANGE[] = "ToolBarTriggerModeChange"; +inline constexpr char EVENT_TOOLBAR_PROJECT_SETTINGS[] = "ToolBarTriggerProjectSettings"; +inline constexpr char EVENT_TOOLBAR_RUN_PROJECT[] = "ToolBarRunProject"; +inline constexpr char EVENT_TOOLBAR_GO_FORWARD[] = "ToolBarGoForward"; +inline constexpr char EVENT_TOOLBAR_GO_BACKWARD[] = "ToolBarGoBackward"; +inline constexpr char EVENT_TOOLBAR_OPEN_FILE[] = "ToolBarOpenFile"; +inline constexpr char EVENT_TOOLBAR_CLOSE_DOCUMENT[] = "ToolBarCloseCurrentDocument"; +inline constexpr char EVENT_TOOLBAR_SHARE_APPLICATION[] = "ToolBarShareApplication"; +inline constexpr char EVENT_TOOLBAR_SET_CURRENT_WORKSPACE[] = "ToolBarSetCurrentWorkspace"; +inline constexpr char EVENT_TOOLBAR_EDIT_GLOBAL_ANNOTATION[] = "ToolBarEditGlobalAnnotation"; +inline constexpr char EVENT_STATUSBAR_SHOW_ZOOM[] = "StatusBarShowZoomMenu"; +inline constexpr char EVENT_STATUSBAR_SET_STYLE[] = "StatusBarSetCurrentStyle"; -const char PROPERTY_EDITOR_CLASSNAME_PROPERTY[] = "__classNamePrivateInternal"; +inline constexpr char PROPERTY_EDITOR_CLASSNAME_PROPERTY[] = "__classNamePrivateInternal"; // Copy/Paste Headers -const char HEADER_3DPASTE_CONTENT[] = "// __QmlDesigner.Editor3D.Paste__ \n"; +inline constexpr char HEADER_3DPASTE_CONTENT[] = "// __QmlDesigner.Editor3D.Paste__ \n"; -const char OBJECT_NAME_ASSET_LIBRARY[] = "QQuickWidgetAssetLibrary"; -const char OBJECT_NAME_CONTENT_LIBRARY[] = "QQuickWidgetContentLibrary"; -const char OBJECT_NAME_BUSY_INDICATOR[] = "QQuickWidgetBusyIndicator"; -const char OBJECT_NAME_COMPONENT_LIBRARY[] = "QQuickWidgetComponentLibrary"; -const char OBJECT_NAME_EFFECT_COMPOSER[] = "QQuickWidgetEffectComposer"; -const char OBJECT_NAME_MATERIAL_BROWSER[] = "QQuickWidgetMaterialBrowser"; -const char OBJECT_NAME_MATERIAL_EDITOR[] = "QQuickWidgetMaterialEditor"; -const char OBJECT_NAME_PROPERTY_EDITOR[] = "QQuickWidgetPropertyEditor"; -const char OBJECT_NAME_COLLECTION_EDITOR[] = "QQuickWidgetQDSCollectionEditor"; -const char OBJECT_NAME_STATES_EDITOR[] = "QQuickWidgetStatesEditor"; -const char OBJECT_NAME_TEXTURE_EDITOR[] = "QQuickWidgetTextureEditor"; -const char OBJECT_NAME_TOP_TOOLBAR[] = "QQuickWidgetTopToolbar"; -const char OBJECT_NAME_STATUSBAR[] = "QQuickWidgetStatusbar"; -const char OBJECT_NAME_TOP_FEEDBACK[] = "QQuickWidgetQDSFeedback"; -const char OBJECT_NAME_NEW_DIALOG[] = "QQuickWidgetQDSNewDialog"; -const char OBJECT_NAME_SPLASH_SCREEN[] = "QQuickWidgetSplashScreen"; -const char OBJECT_NAME_WELCOME_PAGE[] = "QQuickWidgetQDSWelcomePage"; -const char OBJECT_NAME_CONNECTION_EDITOR[] = "QQuickWidgetConnectionEditor"; +inline constexpr char OBJECT_NAME_ASSET_LIBRARY[] = "QQuickWidgetAssetLibrary"; +inline constexpr char OBJECT_NAME_CONTENT_LIBRARY[] = "QQuickWidgetContentLibrary"; +inline constexpr char OBJECT_NAME_BUSY_INDICATOR[] = "QQuickWidgetBusyIndicator"; +inline constexpr char OBJECT_NAME_COMPONENT_LIBRARY[] = "QQuickWidgetComponentLibrary"; +inline constexpr char OBJECT_NAME_EFFECT_COMPOSER[] = "QQuickWidgetEffectComposer"; +inline constexpr char OBJECT_NAME_MATERIAL_BROWSER[] = "QQuickWidgetMaterialBrowser"; +inline constexpr char OBJECT_NAME_MATERIAL_EDITOR[] = "QQuickWidgetMaterialEditor"; +inline constexpr char OBJECT_NAME_PROPERTY_EDITOR[] = "QQuickWidgetPropertyEditor"; +inline constexpr char OBJECT_NAME_COLLECTION_EDITOR[] = "QQuickWidgetQDSCollectionEditor"; +inline constexpr char OBJECT_NAME_STATES_EDITOR[] = "QQuickWidgetStatesEditor"; +inline constexpr char OBJECT_NAME_TEXTURE_EDITOR[] = "QQuickWidgetTextureEditor"; +inline constexpr char OBJECT_NAME_TOP_TOOLBAR[] = "QQuickWidgetTopToolbar"; +inline constexpr char OBJECT_NAME_STATUSBAR[] = "QQuickWidgetStatusbar"; +inline constexpr char OBJECT_NAME_TOP_FEEDBACK[] = "QQuickWidgetQDSFeedback"; +inline constexpr char OBJECT_NAME_NEW_DIALOG[] = "QQuickWidgetQDSNewDialog"; +inline constexpr char OBJECT_NAME_SPLASH_SCREEN[] = "QQuickWidgetSplashScreen"; +inline constexpr char OBJECT_NAME_WELCOME_PAGE[] = "QQuickWidgetQDSWelcomePage"; +inline constexpr char OBJECT_NAME_CONNECTION_EDITOR[] = "QQuickWidgetConnectionEditor"; -const char ENVIRONMENT_SHOW_QML_ERRORS[] = "QMLDESIGNER_SHOW_QML_ERRORS"; +inline constexpr char ENVIRONMENT_SHOW_QML_ERRORS[] = "QMLDESIGNER_SHOW_QML_ERRORS"; namespace Internal { enum { debug = 0 }; From d68ad36ce1a360bf810b01ec27eaf9b474e18a26 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Tue, 20 Feb 2024 13:24:46 +0100 Subject: [PATCH 075/176] QmlDesigner: Fix clang format file The regular expression was case insensitive. Change-Id: Ic44ac8b65428574fb7d0cb8327b8b536b74720d8 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen --- src/plugins/qmldesigner/.clang-format | 5 +++-- tests/unit/.clang-format | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/plugins/qmldesigner/.clang-format b/src/plugins/qmldesigner/.clang-format index 9986740c63e..d3695ac2982 100644 --- a/src/plugins/qmldesigner/.clang-format +++ b/src/plugins/qmldesigner/.clang-format @@ -17,8 +17,9 @@ BreakConstructorInitializers: BeforeComma BreakInheritanceList: AfterComma ColumnLimit: 100 IncludeCategories: - - Regex: '^ Date: Tue, 20 Feb 2024 18:56:42 +0100 Subject: [PATCH 076/176] CMake: introduce option BUILD_DESIGNSTUDIO This change needs an update of the super repository If you want to buid QtDesignStudio documentation no branding path is necessary anymore: cmake -DCMAKE_GENERATOR:STRING=Ninja -DWITH_DOCS=ON -DCMAKE_PREFIX_PATH:PATH=C:\Qt_online\6.5.3\msvc2019_64 -DBUILD_DESIGNSTUDIO=ON C:/dev/tqtc-qtc-super/qtcreator cmake --build . --target html_docs Task-number: QDS-9827 Task-number: QTCREATORBUG-24222 Change-Id: Ic779b6ab57c71c7f6fa53467bf1ba3ee17cab0ea Reviewed-by: Tim Jenssen --- CMakeLists.txt | 5 +++ dist/branding/qtdesignstudio/CMakeLists.txt | 1 - .../qtdesignstudio/QtCreatorIDEBranding.cmake | 45 +++++++++++++++++++ .../QtProject/QtDesignStudio.ini | 1 + .../src/qtcreator-documentation.qdoc | 9 ++-- doc/qtdesignstudio/README.md | 20 +-------- .../qtcreator/qmldesigner}/EasingCurves.ini | 0 src/plugins/qmldesigner/CMakeLists.txt | 13 ++++++ .../timelineeditor/preseteditor.cpp | 2 +- 9 files changed, 70 insertions(+), 26 deletions(-) delete mode 100644 dist/branding/qtdesignstudio/CMakeLists.txt rename {dist/branding/qtdesignstudio/QtProject/qtdesignstudio => share/qtcreator/qmldesigner}/EasingCurves.ini (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2aefb1a58dd..63ca493c29a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,10 @@ cmake_minimum_required(VERSION 3.16) +option(BUILD_DESIGNSTUDIO "Build and install design studio plugins, examples and settings." OFF) +if (BUILD_DESIGNSTUDIO) + list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/dist/branding/qtdesignstudio") +endif() + ## Add paths to check for cmake modules: list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") include(Utils) diff --git a/dist/branding/qtdesignstudio/CMakeLists.txt b/dist/branding/qtdesignstudio/CMakeLists.txt deleted file mode 100644 index 7886ac3ef6d..00000000000 --- a/dist/branding/qtdesignstudio/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -install(DIRECTORY QtProject DESTINATION "${IDE_DATA_PATH}") diff --git a/dist/branding/qtdesignstudio/QtCreatorIDEBranding.cmake b/dist/branding/qtdesignstudio/QtCreatorIDEBranding.cmake index e2131bcc757..204cedffdb1 100644 --- a/dist/branding/qtdesignstudio/QtCreatorIDEBranding.cmake +++ b/dist/branding/qtdesignstudio/QtCreatorIDEBranding.cmake @@ -16,3 +16,48 @@ set(IDE_DOC_FILE_ONLINE "qtdesignstudio/qtdesignstudio-online.qdocconf") set(IDE_ICON_PATH "${CMAKE_CURRENT_LIST_DIR}") set(IDE_LOGO_PATH "${CMAKE_CURRENT_LIST_DIR}") + +set(DESIGNSTUDIO_PLUGINS + Android + BareMetal + Boot2Qt + CMakeProjectManager + CodePaster + Core + CppEditor + Debugger + Designer + DiffEditor + EffectComposer + Help + Insight + LanguageClient + McuSupport + ProjectExplorer + QmakeProjectManager + QmlDesigner + QmlDesignerBase + QmlJSEditor + QmlJSTools + QmlPreview + QmlProjectManager + QtSupport + RemoteLinux + ResourceEditor + StudioPlugin + StudioWelcome + Texteditor + UpdateInfo + VcsBase + assetexporterplugin + componentsplugin + qmlpreviewplugin + qtquickplugin) + +if(DESIGNSTUDIO_EXTRAPLUGINS) + list(APPEND DESIGNSTUDIO_PLUGINS ${DESIGNSTUDIO_EXTRAPLUGINS}) +endif() + +if(NOT BUILD_PLUGINS) + set(BUILD_PLUGINS "${DESIGNSTUDIO_PLUGINS}" CACHE STRING "Build plugins" FORCE) +endif() diff --git a/dist/branding/qtdesignstudio/QtProject/QtDesignStudio.ini b/dist/branding/qtdesignstudio/QtProject/QtDesignStudio.ini index 4685e29f3bd..a48e5ded50a 100644 --- a/dist/branding/qtdesignstudio/QtProject/QtDesignStudio.ini +++ b/dist/branding/qtdesignstudio/QtProject/QtDesignStudio.ini @@ -52,6 +52,7 @@ flashActionDisabled=true OverrideLanguage=C [General] HideOptionCategories=C++, Debug, Designer, Kits, BuildAndRun, CPaster, LanguageClient, Version Control +SuppressedWarnings=LinkWithQtInstallation [Help] ContextHelpOption=3 diff --git a/doc/qtcreatordev/src/qtcreator-documentation.qdoc b/doc/qtcreatordev/src/qtcreator-documentation.qdoc index 9e567c6fd4f..ffed1871a57 100644 --- a/doc/qtcreatordev/src/qtcreator-documentation.qdoc +++ b/doc/qtcreatordev/src/qtcreator-documentation.qdoc @@ -387,8 +387,7 @@ separate doc build folder, not in the project folder. To get the correct product name and version when building \QDS Manual, you - must run CMake with the branding option. The branding data is located in - the \QDS (private) repository, \c tqtc-plugin-qtquickdesigner. + must run CMake with the BUILD_DESIGNSTUDIO option. To build docs with CMake in a separate doc build folder: @@ -408,12 +407,12 @@ \li To also build Extending \QC Manual, add the following option: \c {-DBUILD_DEVELOPER_DOCS=ON} \li To also build the \QDS Manual, add the following option: - \c {"-DCMAKE_MODULE_PATH=/studiodata/branding/"} + \c {-DBUILD_DESIGNSTUDIO=ON} For example: \badcode C:\dev\qtc-doc-build>cmake -DWITH_DOCS=ON -DBUILD_DEVELOPER_DOCS=ON - "-DCMAKE_MODULE_PATH=C:\dev\tqtc-plugin-qtquickdesigner\studiodata\branding" + -DBUILD_DESIGNSTUDIO=ON "-DCMAKE_PREFIX_PATH=C:\Qt\6.4.0\msvc2019_64" C:\dev\qtc-super\qtcreator \endcode @@ -425,7 +424,7 @@ \badcode C:\dev\qtc-doc-build>cmake -DWITH_ONLINE_DOCS=ON -DBUILD_DEVELOPER_DOCS=ON - "-DCMAKE_MODULE_PATH=C:\dev\tqtc-plugin-qtquickdesigner\studiodata\branding" + -DBUILD_DESIGNSTUDIO=ON "-DCMAKE_PREFIX_PATH=C:\Qt\6.4.0\msvc2019_64" C:\dev\qtc-super\qtcreator \endcode diff --git a/doc/qtdesignstudio/README.md b/doc/qtdesignstudio/README.md index c23a21a9a2f..bbb46d13bb8 100644 --- a/doc/qtdesignstudio/README.md +++ b/doc/qtdesignstudio/README.md @@ -7,22 +7,4 @@ separate table of contents files for each Manual and by using defines to hide and show information depending on which Manual is being built. Because branding information is needed to use the correct product name and -version, you must run `qmake -r` on `qtcreator.pro` with the `IDE_BRANDING_PRI` -option set to the absolute path of `ide_branding.pri` in the Qt Design Studio -repository. - -For example, on Windows enter (all on one line): - -`C:\dev\qtc-super\qtcreator>..\..\..\Qt\5.14.1\msvc2017_64\bin\qmake.exe - qtcreator.pro -r - IDE_BRANDING_PRI=C:\dev\tqtc-plugin-qtquickdesigner\studiodata\branding\ide_branding.pri` - -## Building the Qt Design Studio Manual - -1. Run `qmake` from Qt 5.14.0, or later with the path to the branding - information as a parameter: - `\qmake.exe qtcreator.pro -r IDE_BRANDING_PRI=\tqtc-plugin-qtquickdesigner\studiodata\branding\ide_branding.pri` -5. Run `make docs` on Linux and macOS or `nmake docs` on Windows. - -The docs are generated in `qtcreator\doc\html\qtdesignstudio` with the -Qt Design Studio branding. +version. diff --git a/dist/branding/qtdesignstudio/QtProject/qtdesignstudio/EasingCurves.ini b/share/qtcreator/qmldesigner/EasingCurves.ini similarity index 100% rename from dist/branding/qtdesignstudio/QtProject/qtdesignstudio/EasingCurves.ini rename to share/qtcreator/qmldesigner/EasingCurves.ini diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 0ca1db57516..f520495d7c1 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -1243,3 +1243,16 @@ extend_qtc_plugin(qtquickplugin qtquickplugin.cpp qtquickplugin.h qtquickplugin.qrc ) + +if (BUILD_DESIGNSTUDIO) + configure_file( + "${QtCreator_SOURCE_DIR}/dist/branding/qtdesignstudio/QtProject/QtDesignStudio.ini" + "${PROJECT_BINARY_DIR}/${IDE_DATA_PATH}/QtProject/${IDE_CASED_ID}.ini" + COPYONLY + ) + install(FILES + ${QtCreator_SOURCE_DIR}/dist/branding/qtdesignstudio/QtProject/QtDesignStudio.ini + DESTINATION ${IDE_DATA_PATH}/QtProject + RENAME ${IDE_CASED_ID}.ini + ) +endif () diff --git a/src/plugins/qmldesigner/components/timelineeditor/preseteditor.cpp b/src/plugins/qmldesigner/components/timelineeditor/preseteditor.cpp index 0f12c6ce2de..b06023242f1 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/preseteditor.cpp +++ b/src/plugins/qmldesigner/components/timelineeditor/preseteditor.cpp @@ -137,7 +137,7 @@ const char settingsFileName[] = "EasingCurves.ini"; QString settingsFullFilePath(const QSettings::Scope &scope) { if (scope == QSettings::SystemScope) - return Core::ICore::installerResourcePath(settingsFileName).toString(); + return Core::ICore::resourcePath("qmldesigner/%1").toString().arg(settingsFileName); return Core::ICore::userResourcePath(settingsFileName).toString(); } From 4034d9e874b754ac0b95cfde1782a4351b0d73e7 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Wed, 21 Feb 2024 10:07:40 +0200 Subject: [PATCH 077/176] QmlDesigner: Change-Id: I0c8a1b5d76e8509fb0a61ccb0bd8ac62d432d2bb Reviewed-by: hjk --- .../qmldesigner/components/componentcore/qmleditormenu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/components/componentcore/qmleditormenu.cpp b/src/plugins/qmldesigner/components/componentcore/qmleditormenu.cpp index e59fca7353e..62916a44bd2 100644 --- a/src/plugins/qmldesigner/components/componentcore/qmleditormenu.cpp +++ b/src/plugins/qmldesigner/components/componentcore/qmleditormenu.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0#include "qmleditormenu.h" +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 #include "qmleditormenu.h" From ba4fc4774b18f666350540913d00b6034f17a379 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Tue, 20 Feb 2024 18:05:38 +0100 Subject: [PATCH 078/176] QmlDesigner: Move more component code to utils3d Change-Id: I52f6d28bea352922e77b2107977b70257e868a5b Reviewed-by: Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen Reviewed-by: Miikka Heikkinen --- .../assetslibrary/assetslibrarywidget.cpp | 2 +- .../components/componentcore/utils3d.cpp | 91 +++++++++++++++++++ .../components/componentcore/utils3d.h | 7 ++ .../contentlibrary/contentlibraryview.cpp | 14 +-- .../qmldesigner/components/createtexture.cpp | 4 +- .../materialbrowser/materialutils.cpp | 3 +- .../materialeditor/materialeditorview.cpp | 16 ++-- .../navigator/navigatortreemodel.cpp | 9 +- .../textureeditor/textureeditorview.cpp | 16 ++-- .../designercore/include/abstractview.h | 5 - .../designercore/model/abstractview.cpp | 86 ------------------ 11 files changed, 135 insertions(+), 118 deletions(-) diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp index 775505c827c..067cff57a49 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp @@ -247,7 +247,7 @@ void AssetsLibraryWidget::addLightProbe(const QString &filePath) void AssetsLibraryWidget::updateContextMenuActionsEnableState() { - setHasMaterialLibrary(m_assetsView->materialLibraryNode().isValid() + setHasMaterialLibrary(Utils3D::materialLibraryNode(m_assetsView).isValid() && m_assetsView->model()->hasImport("QtQuick3D")); ModelNode activeSceneEnv = m_createTextures.resolveSceneEnv( diff --git a/src/plugins/qmldesigner/components/componentcore/utils3d.cpp b/src/plugins/qmldesigner/components/componentcore/utils3d.cpp index c7653c971f3..54618604df2 100644 --- a/src/plugins/qmldesigner/components/componentcore/utils3d.cpp +++ b/src/plugins/qmldesigner/components/componentcore/utils3d.cpp @@ -3,6 +3,12 @@ #include "utils3d.h" +#include +#include +#include +#include +#include + namespace QmlDesigner { namespace Utils3D { @@ -30,5 +36,90 @@ qint32 active3DSceneId(Model *model) return -1; } +ModelNode materialLibraryNode(AbstractView *view) +{ + return view->modelNodeForId(Constants::MATERIAL_LIB_ID); +} + +// Creates material library if it doesn't exist and moves any existing materials into it. +void ensureMaterialLibraryNode(AbstractView *view) +{ + ModelNode matLib = view->modelNodeForId(Constants::MATERIAL_LIB_ID); + if (matLib.isValid() + || (!view->rootModelNode().metaInfo().isQtQuick3DNode() + && !view->rootModelNode().metaInfo().isQtQuickItem())) { + return; + } + + view->executeInTransaction(__FUNCTION__, [&] { + // Create material library node +#ifdef QDS_USE_PROJECTSTORAGE + TypeName nodeTypeName = view->rootModelNode().metaInfo().isQtQuick3DNode() ? "Node" : "Item"; + matLib = view->createModelNode(nodeTypeName, -1, -1); +#else + auto nodeType = view->rootModelNode().metaInfo().isQtQuick3DNode() + ? view->model()->qtQuick3DNodeMetaInfo() + : view->model()->qtQuickItemMetaInfo(); + matLib = view->createModelNode(nodeType.typeName(), nodeType.majorVersion(), nodeType.minorVersion()); +#endif + matLib.setIdWithoutRefactoring(Constants::MATERIAL_LIB_ID); + view->rootModelNode().defaultNodeListProperty().reparentHere(matLib); + }); + + // Do the material reparentings in different transaction to work around issue QDS-8094 + view->executeInTransaction(__FUNCTION__, [&] { + const QList materials = view->rootModelNode().subModelNodesOfType( + view->model()->qtQuick3DMaterialMetaInfo()); + if (!materials.isEmpty()) { + // Move all materials to under material library node + for (const ModelNode &node : materials) { + // If material has no name, set name to id + QString matName = node.variantProperty("objectName").value().toString(); + if (matName.isEmpty()) { + VariantProperty objNameProp = node.variantProperty("objectName"); + objNameProp.setValue(node.id()); + } + + matLib.defaultNodeListProperty().reparentHere(node); + } + } + }); +} + +bool isPartOfMaterialLibrary(const ModelNode &node) +{ + if (!node.isValid()) + return false; + + ModelNode matLib = materialLibraryNode(node.view()); + + return matLib.isValid() + && (node == matLib + || (node.hasParentProperty() && node.parentProperty().parentModelNode() == matLib)); +} + +ModelNode getTextureDefaultInstance(const QString &source, AbstractView *view) +{ + ModelNode matLib = materialLibraryNode(view); + if (!matLib.isValid()) + return {}; + + const QList matLibNodes = matLib.directSubModelNodes(); + for (const ModelNode &tex : matLibNodes) { + if (tex.isValid() && tex.metaInfo().isQtQuick3DTexture()) { + const QList props = tex.properties(); + if (props.size() != 1) + continue; + const AbstractProperty &prop = props[0]; + if (prop.name() == "source" && prop.isVariantProperty() + && prop.toVariantProperty().value().toString() == source) { + return tex; + } + } + } + + return {}; +} + } // namespace Utils3D } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/componentcore/utils3d.h b/src/plugins/qmldesigner/components/componentcore/utils3d.h index 2a56b4fcf82..9891a62b4cd 100644 --- a/src/plugins/qmldesigner/components/componentcore/utils3d.h +++ b/src/plugins/qmldesigner/components/componentcore/utils3d.h @@ -1,6 +1,8 @@ // 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 + // please put here 3d related functions which have no clear place elsewhere #include @@ -14,5 +16,10 @@ inline constexpr AuxiliaryDataKeyView active3dSceneProperty{AuxiliaryDataType::T ModelNode active3DSceneNode(AbstractView *view); qint32 active3DSceneId(Model *model); +ModelNode materialLibraryNode(AbstractView *view); +void ensureMaterialLibraryNode(AbstractView *view); +bool isPartOfMaterialLibrary(const ModelNode &node); +ModelNode getTextureDefaultInstance(const QString &source, AbstractView *view); + } // namespace Utils3D } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp index fedec758962..61ae078ea8d 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp @@ -120,7 +120,7 @@ WidgetInfo ContentLibraryView::widgetInfo() [&] (const QmlDesigner::TypeName &type) { // delete instances of the bundle material that is about to be unimported executeInTransaction("ContentLibraryView::widgetInfo", [&] { - ModelNode matLib = materialLibraryNode(); + ModelNode matLib = Utils3D::materialLibraryNode(this); if (!matLib.isValid()) return; @@ -222,7 +222,7 @@ void ContentLibraryView::modelAttached(Model *model) updateBundlesQuick3DVersion(); updateBundleMaterialsImportedState(); - const bool hasLibrary = materialLibraryNode().isValid(); + const bool hasLibrary = Utils3D::materialLibraryNode(this).isValid(); m_widget->setHasMaterialLibrary(hasLibrary); m_widget->setHasQuick3DImport(m_hasQuick3DImport); m_widget->setIsQt6Project(externalDependencies().isQt6Project()); @@ -289,7 +289,7 @@ void ContentLibraryView::customNotification(const AbstractView *view, return; if (identifier == "drop_bundle_material") { - ModelNode matLib = materialLibraryNode(); + ModelNode matLib = Utils3D::materialLibraryNode(this); if (!matLib.isValid()) return; @@ -311,7 +311,7 @@ void ContentLibraryView::customNotification(const AbstractView *view, m_draggedBundleMaterial = nullptr; } else if (identifier == "drop_bundle_texture") { - ModelNode matLib = materialLibraryNode(); + ModelNode matLib = Utils3D::materialLibraryNode(this); if (!matLib.isValid()) return; @@ -454,7 +454,7 @@ void ContentLibraryView::applyBundleMaterialToDropTarget(const ModelNode &bundle ModelNode ContentLibraryView::getBundleMaterialDefaultInstance(const TypeName &type) { - ModelNode matLib = materialLibraryNode(); + ModelNode matLib = Utils3D::materialLibraryNode(this); if (!matLib.isValid()) return {}; @@ -480,7 +480,7 @@ ModelNode ContentLibraryView::getBundleMaterialDefaultInstance(const TypeName &t #ifdef QDS_USE_PROJECTSTORAGE ModelNode ContentLibraryView::createMaterial(const TypeName &typeName) { - ModelNode matLib = materialLibraryNode(); + ModelNode matLib = Utils3D::materialLibraryNode(this); if (!matLib.isValid() || !typeName.size()) return {}; @@ -504,7 +504,7 @@ ModelNode ContentLibraryView::createMaterial(const TypeName &typeName) #else ModelNode ContentLibraryView::createMaterial(const NodeMetaInfo &metaInfo) { - ModelNode matLib = materialLibraryNode(); + ModelNode matLib = Utils3D::materialLibraryNode(this); if (!matLib.isValid() || !metaInfo.isValid()) return {}; diff --git a/src/plugins/qmldesigner/components/createtexture.cpp b/src/plugins/qmldesigner/components/createtexture.cpp index b48ff6d0f8c..1363868f37f 100644 --- a/src/plugins/qmldesigner/components/createtexture.cpp +++ b/src/plugins/qmldesigner/components/createtexture.cpp @@ -74,7 +74,7 @@ ModelNode CreateTexture::createTextureFromImage(const Utils::FilePath &assetPat if (mode != AddTextureMode::Texture && mode != AddTextureMode::LightProbe) return {}; - ModelNode matLib = m_view->materialLibraryNode(); + ModelNode matLib = Utils3D::materialLibraryNode(m_view); if (!matLib.isValid()) return {}; @@ -82,7 +82,7 @@ ModelNode CreateTexture::createTextureFromImage(const Utils::FilePath &assetPat QString textureSource = assetPath.relativePathFrom(DocumentManager::currentFilePath()).toString(); - ModelNode newTexNode = m_view->getTextureDefaultInstance(textureSource); + ModelNode newTexNode = Utils3D::getTextureDefaultInstance(textureSource, m_view); if (!newTexNode.isValid()) { newTexNode = m_view->createModelNode("QtQuick3D.Texture", metaInfo.majorVersion(), diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialutils.cpp b/src/plugins/qmldesigner/components/materialbrowser/materialutils.cpp index 76924bedbcd..f22fdc030e4 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialutils.cpp +++ b/src/plugins/qmldesigner/components/materialbrowser/materialutils.cpp @@ -8,6 +8,7 @@ #include "nodemetainfo.h" #include "qmlobjectnode.h" #include "variantproperty.h" +#include #include @@ -24,7 +25,7 @@ void MaterialUtils::assignMaterialTo3dModel(AbstractView *view, const ModelNode { QTC_ASSERT(modelNode.isValid() && modelNode.metaInfo().isQtQuick3DModel(), return); - ModelNode matLib = view->materialLibraryNode(); + ModelNode matLib = Utils3D::materialLibraryNode(view); if (!matLib.isValid()) return; diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp index 5490829bb5e..ab43e992f7a 100644 --- a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp +++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp @@ -25,6 +25,7 @@ #include "qmltimeline.h" #include "variantproperty.h" #include +#include #include #include @@ -54,9 +55,10 @@ MaterialEditorView::MaterialEditorView(ExternalDependenciesInterface &externalDe && model()->rewriterView()->errors().isEmpty()) { DesignDocument *doc = QmlDesignerPlugin::instance()->currentDesignDocument(); if (doc && !doc->inFileComponentModelActive()) - ensureMaterialLibraryNode(); + Utils3D::ensureMaterialLibraryNode(this); if (m_qmlBackEnd && m_qmlBackEnd->contextObject()) - m_qmlBackEnd->contextObject()->setHasMaterialLibrary(materialLibraryNode().isValid()); + m_qmlBackEnd->contextObject()->setHasMaterialLibrary( + Utils3D::materialLibraryNode(this).isValid()); m_ensureMatLibTimer.stop(); } }); @@ -414,7 +416,7 @@ void MaterialEditorView::handleToolBarAction(int action) if (!model()) break; executeInTransaction(__FUNCTION__, [&] { - ModelNode matLib = materialLibraryNode(); + ModelNode matLib = Utils3D::materialLibraryNode(this); if (!matLib.isValid()) return; @@ -529,7 +531,8 @@ void MaterialEditorView::setupQmlBackend() QString specificQmlData; QString currentTypeName; - if (m_selectedMaterial.isValid() && m_hasQuick3DImport && (materialLibraryNode().isValid() || m_hasMaterialRoot)) { + if (m_selectedMaterial.isValid() && m_hasQuick3DImport + && (Utils3D::materialLibraryNode(this).isValid() || m_hasMaterialRoot)) { qmlPaneUrl = QUrl::fromLocalFile(materialEditorResourcesPath() + "/MaterialEditorPane.qml"); TypeName diffClassName; @@ -582,7 +585,8 @@ void MaterialEditorView::setupQmlBackend() currentQmlBackend->widget()->installEventFilter(this); currentQmlBackend->contextObject()->setHasQuick3DImport(m_hasQuick3DImport); - currentQmlBackend->contextObject()->setHasMaterialLibrary(materialLibraryNode().isValid()); + currentQmlBackend->contextObject()->setHasMaterialLibrary( + Utils3D::materialLibraryNode(this).isValid()); currentQmlBackend->contextObject()->setSpecificQmlData(specificQmlData); currentQmlBackend->contextObject()->setCurrentType(currentTypeName); currentQmlBackend->contextObject()->setIsQt6Project(externalDependencies().isQt6Project()); @@ -1028,7 +1032,7 @@ void MaterialEditorView::duplicateMaterial(const ModelNode &material) QList dynamicProps; executeInTransaction(__FUNCTION__, [&] { - ModelNode matLib = materialLibraryNode(); + ModelNode matLib = Utils3D::materialLibraryNode(this); if (!matLib.isValid()) return; diff --git a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp index 5ae3d5d7207..d305753ead3 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp +++ b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp @@ -23,10 +23,11 @@ #include #include #include -#include -#include #include #include +#include +#include +#include #include @@ -935,11 +936,11 @@ bool NavigatorTreeModel::setData(const QModelIndex &index, const QVariant &value if (index.column() == ColumnType::Alias && role == Qt::CheckStateRole) { m_view->handleChangedExport(modelNode, value.toInt() != 0); } else if (index.column() == ColumnType::Visibility && role == Qt::CheckStateRole) { - if (m_view->isPartOfMaterialLibrary(modelNode) || QmlItemNode(modelNode).isEffectItem()) + if (Utils3D::isPartOfMaterialLibrary(modelNode) || QmlItemNode(modelNode).isEffectItem()) return false; QmlVisualNode(modelNode).setVisibilityOverride(value.toInt() == 0); } else if (index.column() == ColumnType::Lock && role == Qt::CheckStateRole) { - if (m_view->isPartOfMaterialLibrary(modelNode)) + if (Utils3D::isPartOfMaterialLibrary(modelNode)) return false; modelNode.setLocked(value.toInt() != 0); } diff --git a/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp b/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp index 47d85c4dbc3..5cdd788ab7b 100644 --- a/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp +++ b/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -65,9 +66,10 @@ TextureEditorView::TextureEditorView(AsynchronousImageCache &imageCache, && model()->rewriterView()->errors().isEmpty()) { DesignDocument *doc = QmlDesignerPlugin::instance()->currentDesignDocument(); if (doc && !doc->inFileComponentModelActive()) - ensureMaterialLibraryNode(); + Utils3D::ensureMaterialLibraryNode(this); if (m_qmlBackEnd && m_qmlBackEnd->contextObject()) - m_qmlBackEnd->contextObject()->setHasMaterialLibrary(materialLibraryNode().isValid()); + m_qmlBackEnd->contextObject()->setHasMaterialLibrary( + Utils3D::materialLibraryNode(this).isValid()); m_ensureMatLibTimer.stop(); } }); @@ -375,7 +377,7 @@ void TextureEditorView::handleToolBarAction(int action) if (!model()) break; executeInTransaction("TextureEditorView:handleToolBarAction", [&] { - ModelNode matLib = materialLibraryNode(); + ModelNode matLib = Utils3D::materialLibraryNode(this); if (!matLib.isValid()) return; @@ -410,7 +412,8 @@ void TextureEditorView::setupQmlBackend() QUrl qmlSpecificsUrl; QString specificQmlData; - if (m_selectedTexture.isValid() && m_hasQuick3DImport && (materialLibraryNode().isValid() || m_hasTextureRoot)) { + if (m_selectedTexture.isValid() && m_hasQuick3DImport + && (Utils3D::materialLibraryNode(this).isValid() || m_hasTextureRoot)) { qmlPaneUrl = QUrl::fromLocalFile(textureEditorResourcesPath() + "/TextureEditorPane.qml"); TypeName diffClassName; @@ -457,7 +460,8 @@ void TextureEditorView::setupQmlBackend() currentQmlBackend->widget()->installEventFilter(this); currentQmlBackend->contextObject()->setHasQuick3DImport(m_hasQuick3DImport); - currentQmlBackend->contextObject()->setHasMaterialLibrary(materialLibraryNode().isValid()); + currentQmlBackend->contextObject()->setHasMaterialLibrary( + Utils3D::materialLibraryNode(this).isValid()); currentQmlBackend->contextObject()->setSpecificQmlData(specificQmlData); bool hasValidSelection = QmlObjectNode(m_selectedModel).hasBindingProperty("materials"); currentQmlBackend->contextObject()->setHasSingleModelSelection(hasValidSelection); @@ -745,7 +749,7 @@ void TextureEditorView::duplicateTexture(const ModelNode &texture) QList dynamicProps; executeInTransaction(__FUNCTION__, [&] { - ModelNode matLib = materialLibraryNode(); + ModelNode matLib = Utils3D::materialLibraryNode(this); if (!matLib.isValid()) return; diff --git a/src/plugins/qmldesigner/designercore/include/abstractview.h b/src/plugins/qmldesigner/designercore/include/abstractview.h index 4f1ddbd5700..385739aa829 100644 --- a/src/plugins/qmldesigner/designercore/include/abstractview.h +++ b/src/plugins/qmldesigner/designercore/include/abstractview.h @@ -228,11 +228,6 @@ public: void changeRootNodeType(const TypeName &type, int majorVersion, int minorVersion); - void ensureMaterialLibraryNode(); - ModelNode materialLibraryNode(); - bool isPartOfMaterialLibrary(const ModelNode &node); - ModelNode getTextureDefaultInstance(const QString &source); - const NodeInstanceView *nodeInstanceView() const; RewriterView *rewriterView() const; diff --git a/src/plugins/qmldesigner/designercore/model/abstractview.cpp b/src/plugins/qmldesigner/designercore/model/abstractview.cpp index 37fd6fe8606..aa9422d5c49 100644 --- a/src/plugins/qmldesigner/designercore/model/abstractview.cpp +++ b/src/plugins/qmldesigner/designercore/model/abstractview.cpp @@ -772,92 +772,6 @@ void AbstractView::changeRootNodeType(const TypeName &type, int majorVersion, in m_model.data()->d->changeRootNodeType(type, majorVersion, minorVersion); } -// Creates material library if it doesn't exist and moves any existing materials into it. -void AbstractView::ensureMaterialLibraryNode() -{ - ModelNode matLib = modelNodeForId(Constants::MATERIAL_LIB_ID); - if (matLib.isValid() - || (!rootModelNode().metaInfo().isQtQuick3DNode() - && !rootModelNode().metaInfo().isQtQuickItem())) { - return; - } - - executeInTransaction(__FUNCTION__, [&] { - // Create material library node -#ifdef QDS_USE_PROJECTSTORAGE - TypeName nodeTypeName = rootModelNode().metaInfo().isQtQuick3DNode() ? "Node" : "Item"; - matLib = createModelNode(nodeTypeName, -1, -1); -#else - auto nodeType = rootModelNode().metaInfo().isQtQuick3DNode() - ? model()->qtQuick3DNodeMetaInfo() - : model()->qtQuickItemMetaInfo(); - matLib = createModelNode(nodeType.typeName(), nodeType.majorVersion(), nodeType.minorVersion()); -#endif - matLib.setIdWithoutRefactoring(Constants::MATERIAL_LIB_ID); - rootModelNode().defaultNodeListProperty().reparentHere(matLib); - }); - - // Do the material reparentings in different transaction to work around issue QDS-8094 - executeInTransaction(__FUNCTION__, [&] { - const QList materials = rootModelNode().subModelNodesOfType( - model()->qtQuick3DMaterialMetaInfo()); - if (!materials.isEmpty()) { - // Move all materials to under material library node - for (const ModelNode &node : materials) { - // If material has no name, set name to id - QString matName = node.variantProperty("objectName").value().toString(); - if (matName.isEmpty()) { - VariantProperty objNameProp = node.variantProperty("objectName"); - objNameProp.setValue(node.id()); - } - - matLib.defaultNodeListProperty().reparentHere(node); - } - } - }); -} - -// Returns ModelNode for project's material library if it exists. -ModelNode AbstractView::materialLibraryNode() -{ - return modelNodeForId(Constants::MATERIAL_LIB_ID); -} - -bool AbstractView::isPartOfMaterialLibrary(const ModelNode &node) -{ - if (!node.isValid()) - return false; - - ModelNode matLib = materialLibraryNode(); - - return matLib.isValid() - && (node == matLib - || (node.hasParentProperty() && node.parentProperty().parentModelNode() == matLib)); -} - -ModelNode AbstractView::getTextureDefaultInstance(const QString &source) -{ - ModelNode matLib = materialLibraryNode(); - if (!matLib.isValid()) - return {}; - - const QList matLibNodes = matLib.directSubModelNodes(); - for (const ModelNode &tex : matLibNodes) { - if (tex.isValid() && tex.metaInfo().isQtQuick3DTexture()) { - const QList props = tex.properties(); - if (props.size() != 1) - continue; - const AbstractProperty &prop = props[0]; - if (prop.name() == "source" && prop.isVariantProperty() - && prop.toVariantProperty().value().toString() == source) { - return tex; - } - } - } - - return {}; -} - ModelNode AbstractView::currentStateNode() const { if (m_model) From ec5e60052d2c9370c5501fa4f57ecd70d28fa062 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 21 Feb 2024 11:19:05 +0100 Subject: [PATCH 079/176] QmlDesigner: Improve model node selection tracing Change-Id: I883c3d7e775edc0e0577d51647952680d5685ae1 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen --- src/libs/nanotrace/nanotracehr.h | 8 ++++-- .../qmldesigner/designercore/model/model.cpp | 26 +++++++++++++++---- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/src/libs/nanotrace/nanotracehr.h b/src/libs/nanotrace/nanotracehr.h index 7c83418a34f..d59073ede58 100644 --- a/src/libs/nanotrace/nanotracehr.h +++ b/src/libs/nanotrace/nanotracehr.h @@ -779,6 +779,8 @@ public: template void end(Arguments &&...) {} + + static constexpr bool categoryIsActive() { return Category::isActive(); } }; @@ -873,13 +875,13 @@ public: std::size_t bindId = 0; if (m_id) { - auto category = m_category(); + auto &category = m_category(); bindId = category.createBindId(); category.begin('b', m_id, name, bindId, IsFlow::Out, std::forward(arguments)...); } return {std::piecewise_construct, - std::forward_as_tuple(std::move(name), m_id, m_category), + std::forward_as_tuple(PrivateTag{}, std::move(name), m_id, m_category), std::forward_as_tuple(PrivateTag{}, std::move(name), bindId, m_category)}; } @@ -928,6 +930,8 @@ public: m_id = 0; } + static constexpr bool categoryIsActive() { return Category::isActive(); } + private: StringType m_name; std::size_t m_id = 0; diff --git a/src/plugins/qmldesigner/designercore/model/model.cpp b/src/plugins/qmldesigner/designercore/model/model.cpp index 61f637e4c56..01b2cca3535 100644 --- a/src/plugins/qmldesigner/designercore/model/model.cpp +++ b/src/plugins/qmldesigner/designercore/model/model.cpp @@ -1099,8 +1099,6 @@ void ModelPrivate::notifyNodeOrderChanged(const InternalNodeListProperty *intern void ModelPrivate::setSelectedNodes(const QList &selectedNodeList) { - auto tracer = traceToken.begin("selected model nodes"_t); - auto sortedSelectedList = Utils::filtered(selectedNodeList, [](const auto &node) { return node && node->isValid; }); @@ -1112,14 +1110,32 @@ void ModelPrivate::setSelectedNodes(const QList &selectedNo if (sortedSelectedList == m_selectedInternalNodeList) return; - for (auto &node : sortedSelectedList) { - auto flowToken = node->traceToken.tickWithFlow("select model nodes"_t); - traceToken.tick(flowToken, "select model node"_t); + auto [tracer, flow] = traceToken.beginWithFlow("selected model nodes"_t); + auto &flowToken = flow; // should be not anymore needed in C++ 20 + + if constexpr (decltype(traceToken)::categoryIsActive()) { // the compiler should optimize it away but to be sure + std::set_difference(sortedSelectedList.begin(), + sortedSelectedList.end(), + m_selectedInternalNodeList.begin(), + m_selectedInternalNodeList.end(), + Utils::make_iterator([&](const auto &node) { + node->traceToken.tick(flowToken, "select model node"_t); + })); } const QList lastSelectedNodeList = m_selectedInternalNodeList; m_selectedInternalNodeList = sortedSelectedList; + if constexpr (decltype(traceToken)::categoryIsActive()) { // the compiler should optimize it away but to be sure + std::set_difference(lastSelectedNodeList.begin(), + lastSelectedNodeList.end(), + m_selectedInternalNodeList.begin(), + m_selectedInternalNodeList.end(), + Utils::make_iterator([&](const auto &node) { + node->traceToken.tick(flowToken, "deselect model node"_t); + })); + } + changeSelectedNodes(sortedSelectedList, lastSelectedNodeList); } From 5e4f5d9a219fb38ad4e9405923c5a2e374cb0107 Mon Sep 17 00:00:00 2001 From: Teea Poldsam Date: Wed, 14 Feb 2024 14:41:31 +0200 Subject: [PATCH 080/176] Doc: Update Setting up a 3D Scene tutorial Fixes: QDS-11910 Change-Id: I2ebc117a6caa98aba9846c8cad9b18c835fc10b2 Reviewed-by: Mats Honkamaa --- .../examples/doc/3DsceneTutorial.qdoc | 26 +++++++++--------- .../doc/images/3d-scene-add-light-probe.webp | Bin 54658 -> 18604 bytes .../doc/images/3d-scene-add-texture.webp | Bin 11658 -> 14188 bytes 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/doc/qtdesignstudio/examples/doc/3DsceneTutorial.qdoc b/doc/qtdesignstudio/examples/doc/3DsceneTutorial.qdoc index 37340999157..b89689618e0 100644 --- a/doc/qtdesignstudio/examples/doc/3DsceneTutorial.qdoc +++ b/doc/qtdesignstudio/examples/doc/3DsceneTutorial.qdoc @@ -22,7 +22,7 @@ \li Background images \endlist - The assets you use in this tutorial are available in \uicontrol {Content Library}. + The assets you use in this tutorial are available in the \uicontrol {Content Library} view. To follow this tutorial, you need to first download the starting project from \l{https://git.qt.io/public-demos/qtdesign-studio/-/tree/master/tutorial%20projects/3Dscene%20tutorial/Start}{here}. @@ -45,14 +45,14 @@ \section1 Adding Materials to the 3D Models - First, use materials from \uicontrol {Content Library} on the ball bearing. + First, use materials from the \uicontrol {Content Library} view on the ball bearing. \list 1 \li In the \uicontrol 3D view, right-click the ball bearing and select \uicontrol {Edit Component}. \image 3d-scene-edit-component-menu.png - \li From \uicontrol {Content Library}, drag materials to the different parts of the ball - bearing in the \uicontrol Navigator view. + \li From the \uicontrol {Content Library} view, drag materials to the different parts of + the ball bearing in the \uicontrol Navigator view. Drag the following materials to the following parts: \list \li Chrome to \e inner_race and \e outer_race. @@ -60,8 +60,8 @@ \li Gold to \e retainer. \li Carbon Fiber to \e shield_left and \e shield_right. \endlist - \note The first time you use an asset from \uicontrol {Content Library}, you need to - download it. + \note The first time you use an asset from the \uicontrol {Content Library} view, you + need to download it. \image 3d-scene-drag-material.png \li Select \e {Screen01.ui.qml} in the breadcrumb in the top menu bar to return to the 3D scene. @@ -75,7 +75,7 @@ Environmental lighting is a good way to create a realistic light for your scene. \list 1 - \li In \uicontrol {Content Library}, go to the \uicontrol Environments tab. + \li In the \uicontrol {Content Library} view, go to the \uicontrol Environments tab. \li Right-click the image \e BasicLights3_4k.hdr and select \uicontrol {Add Light Probe}. \image 3d-scene-add-light-probe.webp Setting an image as a light probe for a scene adds the image as the source for the image-based @@ -89,20 +89,20 @@ you don't want to use the skybox. \list 1 \li In the \uicontrol Navigator view, select \e sceneEnvironment. - \li In the \uicontrol Properties view, set \uicontrol {Background Mode} to - \uicontrol Transparent. + \li Go to the \uicontrol {Scene Environment} tab in \uicontrol Properties and set \uicontrol + {Background Mode} to \uicontrol Transparent. \endlist - You also want to increase the brightness of the light a bit. In the \uicontrol Properties view, - set \uicontrol Exposure to 10. + You also want to increase the brightness of the light a bit. Go to the \uicontrol {Image Based + Lighting} tab in \uicontrol Properties and set \uicontrol Exposure to \e 10. \section2 Adding a Background Image to the Scene In the final step of this tutorial, you add a background image to your scene. \list 1 - \li Go to the \uicontrol Textures tab in \uicontrol {Content Library}. - \li Right-click the image \e 4kScratchySurface.png and select \uicontrol {Add Texture} + \li Go to the \uicontrol Textures tab in the \uicontrol {Content Library} view. + \li Right-click the image \e 4kScratchySurface.png and select \uicontrol {Add Texture}. \image 3d-scene-add-texture.webp This adds the image as a texture to the project. It is now available in the \uicontrol Assets view. diff --git a/doc/qtdesignstudio/examples/doc/images/3d-scene-add-light-probe.webp b/doc/qtdesignstudio/examples/doc/images/3d-scene-add-light-probe.webp index 79ea955232b92fbe726b8eb482c60732c8e7cf44..16f65032800db5d1802a111b53935e3a19d88372 100644 GIT binary patch literal 18604 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 literal 54658 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 Y*Q*o6e(axcWOcatqi@qD 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 diff --git a/doc/qtdesignstudio/examples/doc/images/3d-scene-add-texture.webp b/doc/qtdesignstudio/examples/doc/images/3d-scene-add-texture.webp index 3624fa941839ef869ed4cb31e0aa01b8436c3b78..26bea05402c5d4ad9e0ada594dd0e8cb1b1570d1 100644 GIT binary patch delta 14153 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 KaMmc$T2}xbXXi@* delta 11603 zcmaEp*A>kc=B}cwq@niZ)7apU%Hl zKO_H^{;l;D>;Kqq`}cPLiK>RW<5k!G-}&bF@BS;{-~O*7|DV2@{-@l7{r~@*d-W6l zovv?wr~Lc>lkao)CGDNP;9kI6`=9$C$ls|i|F-|n_rLRb>SF4hUkSeH|9by+h1~c0 z?{STC4{JC7eZO$|?fM&E^53%m?JwYeA%DI8%5UcX)`u@&tZl5H^*`-@bg7+!X)U!Z;A<74J+dzOY(Xsb<}(XvQ&x|aT%BLOj=-=;U-oaFu`^zE!G zK|UIDzo&1Tl;xLxC%m7BG1RI-FL-rd*eZGMM{^uYu3Vj~%=+;S{xeY)22(5C4gc07Se`kBfHd_L<(&HN!XF>i1E+n=YkOxCeC z?x;|5S@ibN^qXwY+?q^N79`6(%(=8)F|NIz&->@AnCm8js~*qUA%A}3R6!m2!1^?$ z>bA+pKNwt3N|e8LjN5BM%I^t3R*10>(kW8$H&-n%fAIBAHQ}gaGP~hitb02 zN}+3t<@zm`FWfz?Tg3Nl@9fstYaaFD0#y!vN|mRdE(tg-y;3tZ&tgu%t`y1rG44(~ zQ~z(URP=Itmb}HXw_dp-{%Tt(N6mq2HeLT88O4fKuG4#KFV*qqPU~jARsYV~&fY%d zYT2)>|F2?{Q_UNn=*{U!`tn14Ug%k#;Qyv3V%dEB=FXG9ciG&os_9mKptSrP)BYVV z>(iGSxFz$gbEsHw;qf&7>kS?1ET8r2uJu1TVX#jxXwl9E^PNpE9+dJZD$1-*f41nB z`~DX)jGATcyd^ztnVE2ZvLGAy`l=Fj<=6JHNd4A}- zc#)p9hGfI?XW#U9T2w#PYlz`Wl{>As@)pl`k!yReOWSWxT=wv`BKO=k->*#Dyie)c z_kEuof1YDM#`>;)&8@KBjGgUlxp_L1M3;#-PwGh%yF8Ic_J0>(rOZxfAyt-eIVJ^TkKbE87Jxo++1@TE4_K;BvCOlff>-)&k z z_4ieZ_EVI&{`v&-Yb7Y1<72*@mas%tt3~kTjDEwZ(LHtC?$kPs$k{(C+gnqt{-w!!}#l_HK}(qpFC5a z`s2}wEB|#RZ<-Za{VEqJDEgLv_TATzL`|W69({-2J=%A`>Wr+g;ky~}cOTUpVVQh* zd+D>uJGfUGujY7}rhe;)xo4AOhlP3r&0g+;eaU@>(+9!oBP^Kdu8?=fsH zKPOvO%YO3vHP7-n53v}|cXPBpFClTPruunR{jr6oesI}z8#uN6_++P(QV=N0|6 zf*%u3(tTcb__B8|9(B8~P%M^` z3A+1rvI^72zbmd@FW9{7uS2 zdd-&Ky{V@Ev{R(gp;|HaM$9T5=BJ`BJ74%Dr`5M5Bvomq{e8D$`PF#+H_3|DZ6zIR z7nXl?I=Qa;*s&tBAB?Yq15BUql*m8b{$c`m**&?ZaDUm+-4AxY{J-;okly+jzB2}E zJ&ku=nU=8N(1&G8x8`h9ycT%!+ziIQvP-gcx$Ea-2TogeJu`6K$B*9E*o4hjL|a%t zy0}49?%?ssYxdq!Jy&A5{-`6DfJt7Cmi6`f^)eCn<#~NexzD@Qn|u)av`I~TugC=_ zr;GFNH+;{l<2`3MCh-63b)*lp*|VE*f@d5d3M;_^>XVRj+1J^N3Jb8R~0`83*yXLsl- zx!nN@l}akV?_N2z(ylHnP_2GW!NXkb*{dgsx#!9=|N5Sq<#DB~$%4&b@7YNcPQ1_N zQ_e9p$*gv^;|HBIYD?4OZKX=TYo%PKndC?^C(yPh!BJywA*Pjsy`1R_V zt65S>_T;u!k!8*b3?N|sXm0x=2M~*a?^;5LOOMLI<@1h(#=cqbF(&1Cz00f{R!jcR z;c<4B*fMY7yl;}5&r9n!iG6zb?!)PTzYa1=RW}5VA3M@6bKvzx4_&L6XNIdwIK$k0 zw_eren)^SbT(q`LQ(0@@rp;S+&iv5lr7kryZlX@v+olX}28S5DNhjD9dyXQ@1SZ^^u^B@0- z2+f~QmtTCzle|Oq#a6wY$wrem7{$+csA}Bc=rYOs`Xx0pr#&eq z8!h@mie6TCKYVg+Q+d|3sS9l7qr;a-mmjx$@p{qo=k+?vll{J&<&1XLezJ`3oK(&K z-S?LJ$?0=tP1&ne@lW}j_2In>3-qPe&g|IzB;-Sf={sN5j99knm%a~XB;5F3cGGxi z`EA`TT^vv3*7lw<`*T0_$NkgHtKa)p{dpm%bi>y30Q;_3ZcXK9H@`H!4>8@Pb?BPS z+ts_>l>=-UF6}?iQ!hTzdVkcKfY+;9zVBU^rzCY(u=9>mrS)rvzgO&PuGY<8UaKCk zf0l8@c}>ULWf_b5ye%yA95$bdx@A@OcY4Q99!cw6E{yJh>u>x2@LYNSVYIKu{}Vc@ zTkU_VN@)0HsuUV;Jo9GRk41TslPyEkciXE}tPb7e*8k++!e3R#|4h=WpYLSs@Fef% zv`rQ#=5?Lkx76ms^iBq&UAtdhv);c^^#2{D3gM9ItF`~){Le1R?)2?je~F2wuzdiQ1{Np4OcywLkm8+uh!n|bYwk~Y1mrI&?$@2eY8c5{=C!qquUG7t`^R#veba(DPvF)xUnxzLbp*5}jR6hI)Rk4P~Qo`d7&4@qshZ$$wpLt|SVwpyBHE^+>6J|*RPsZZTTft`G@ z7N5HIF4^A3@agt z*&tT(Mv^5h?)d7rEtA>58D(X7NoMm*Yltv!KW(>N$)azO#rKCRUvlPW>?)J5oT>i# zPQBCmYt9Tl0y&=(3rj9f<8$Mv|7NG;P+X_AGk=c!thkNv189Mzw?saH^kqp6YO!>}ppn@P>9n8^(7l8diYtlQ~b>nCg= zdU8T^)>qLIefi7#YUjRiD*J!PBRbRBPo{qQJcmvbQI!eaPy2TTNaUTAyLS9eU^ zgSiv7@apsO9-n8>^6K5My}B_Q?5FL{RcWhTvhn_YyZDWHEE%_cJ29TL34Z?ZSoWWo zyrNroOIdb>Ug7lXea-Z5W8Km4uPZOf85KdmM#Eak8TUPGg_d|b)QXBKGuC3eWsWgSoId*p9 ze-l3=sVt|T4aP1E49pq4&X-P>&CZ?pqO8l#e$kP54(7j?>reG;JKJ*6$|HO}cXI0< z!Pq+sl7g=1N*%A7;C{Jm3%2|Zn*FYzDX3~a`<|@CA31hjT_<}@Z(YlH_Tkd&=o1kwE`E#O zNQ4{Sf0M#9{a2C1m4dwbg?4!|#VI!y&y-oag}w9t+}ri#8~0AVxG2QA&uB%#r*iqm zfZcOut7^U0e#fL2Cg~l0>0HSh=c<*5KCET2H7VznUbFR+zVW9H(PiEbF5g)%<{|da zx^13)!qlFiWdcE(jfLLgU$=IrMr>Xq{`$6#&DVpUzHA8C^7n(6v12vg<;;4XDISg| zYI{rW8SZ|OwT<(0{4Smi(_)eqZMh|U(~>b=*js*5(bnfiGpg@3hRf~_TvoLsOmyrQX5fj!1YM<$N;2$8`ZU|vQ8wDpA&t$| zfT=$08MFPq)saP&oNIHIefCXRax^>GWERJxI=Q%i$-E|+e(rL1EY1BJ4QKv1z;Qc= zdHYw%z?wA~T5s6g?fd32SF27CyrwS3eT`GXeBqt zV9T7yz0S+Lwno-Dvl%mOjY$8iBD6!`^`I+wNnvV!y`#0sqcTKC` z!4ZzD>eyFp_+2Sow%XOOEP#8*w(fb>YWe2L7WPuEkuFvJc5go0W!sq)s$Kk8ec?>0 zxZuRaatf~*luPS9Oj672KC$q|x3MNBg$8}})=ylVzB*)H&f?b7QoF2fELqPt{czCF z>O<_kzkjv-n`U@?dbwD$j?tp|-QAZi?a)4_rLvXn$__t+h!lD84>cPW`9E6pqyKN% zpYLbCs;`JIsdspN=ytE|qP@*K{tG)`;**L9$$V(#BOpc$ed+hoBY1`iLvJO55EKMiM9#<;1O6P-m<#mfXB<= zU(2@sRm_vdD4Ba zY$`iuRNv!k+T#_X^Y3BEp`KRu)~4qjjgvyeTeKsRj~v?h zZP`IHhUK>{UFtQwV%ld$ru8kY`|eh_P)c}hkm>W&U#?1~d%r(3&$!Cy=HxFD8*>vw z^!Yx8ML+%U!a;xEuOsK%|Fj1S%zv6QjpM&^(A>`ox9;zdGn2AD)va+o|N6TVN87&r zH>_hiq1c;l*O|Rs;g5ww_4$v&g&RBtN=$UC3(gkEM3jpchgxUWJEio-El%rt`y!)i z*Ho@1jk$e0GFsTgVeKlDecPjPX^S@GFcdAm06%d-)F_N&EXSb&ZKKkyBeUjCNUtydW+YVPnE0m0{Tk>+|yMu zn(`%=YHi;@K26VHApD`qy22#$D8?zFHP*<^L{__fp?F1 z;mX&iH-2yqvU_}qi@BaPd7_`^0-0+&svq`73N(4n4pa4d9`WvL^sN@H*Y?|lj?}L# zSp7O-n#tp;yWQHFGC#|IJP8&O+#Q;f<~3LCM^4@3zgK<=iZ~~WD0oQEI#POJ_g1wh ziS4(v7ijf7i>k<-r~4}3;MXdHx~X}NwnafPomOmZ%Ve#VhfTU1{4uq%@#jU?jV|8I zoM&$&$M<=Q%(WHHeX;uAL4|U)L$})cCoOZSuf6TLHN95D{a4_0r{>TL9p}|nvd>K@ zIQ?31#Rc9?$Lth}{5IU`Dm@oi66hnEI-C93gZrBUS7+4S(hIw+vHF^!poN9`8CUO? z@~V$@)>jsaoje|p>1lnYWcR7Zu5)cIC6@eqeS&croB4!~nQEt3zuwySY060+1E<>x zJMGWCp6XM7X2&=?w!B>Qb*F_ zCD!#H?#-VsbNs>mEhh0NKbEKjoD?&ufA;fL-i3o-uFY{{$-n)7-q!==lP2Gs^m#?4 z^D<4AW3{Ql4`(TST`zhk(?3Xc{mgfd7I0>z*?-M)@6_=r+Pb8?UTMQ0yK>V{4UTcf zVy62YD>#oxDH;C$`Hk&_g6jp&sz=(UB_eHKdP+YZ|5cT^YJOmgZrVpL-kXQ2HrR__ z|FMG0Y3jR%haYqjvWwlnXswVqeQ)<&o0xwB<}30-?r)u~Cm?PYI*aSp|C7qzoHMlb z+xTAa+RmQ;K2B5it}u&T@Ynix`#wFCaNB4%)mb6qn<*Ef3-8jbJw?35tIL% zi9NWp`-$uNmHoEML{0Wj%--C;Z2iG5pOu&TR?Nz5^g4rn>&s%;!=z0+{Pa*uDR7HT$GmWje zHtP!REOtH9S$B5h+Cu&_B3ey%?dnga>qhV=r*P*OKK;dN`24yDN7=)}WtI%b8xP&+ zU2Nc#dLhhnOYKQ#KF!PLPQ0E|`u+mrLxYP=m+E(Hc=JnrgTf+>zwg`(&+RULGdt4B zGhMG(dg0!V*5`R`7c7;{UeuTet$!=Ywbfbcx9F2s^*4^inMbc$G(jQ$z_a-`xT{@L zo&OrWd-(ILURJ?9r`ZBJe*)M#F9bOJy0B=?o;3xQKWpyiExVz`phpDS!X7H5C z11BXE!*XvtXLoFLQZJE5H6kd3L(|CmYr@0@h%U@b)cymYAly8?8U6|3g z#=I^*{Y3V<^^FHxRy-66NzgEmo1vo2wxznp`Q5XUE9VO$A3XoAHJMrYbxY!|-3#OI z7hlYnpZKX;?^wZMiS}asn=VIMjJ#)D%RBd^kWcKDcIW=-T%VbG#v` zaqZ-0yY%1NRV;j^o>!~yOkeZ$SMY~PYK8fem!^lkUB6;$ON-Q!`kg;_MS3${i)zk! z^5yW(-If6&8`OUbSva<3NL+1>>Dv&pO2miBI80!wTI2SguRNxkCd~hBW+Pj9-&^AC z<5#aczI|Nt*i3jsZlR~3nq0m8jyXN&g}i!Sohnc9{#=;-;YWrRr*6AyG1rgWm*ICJ z%J{f8PCmyp_2{%uCk#Hmm|6PXkthC|joADTH>2}HvU5uo-WGViKcMWS=Gy)%3i~Tt zZ@;os_6}&6GOPCLlP_ym>STSmyQio6+$@zep@qmVXoGS5>E4lb8fSX{XZ*U%q&$MzwzLNHHH^o zi`Mm=VfPAj$zBokiP%BHG zS6`X_d~s!H>uc2s(JLi#p6?J1X@6h%JMGOWo4-F9o-=7yo%rq)KV3`f_~j=8?vs9= z&{dzJS~au1CYs}Vj*81iDUKe-+tE!WrKi;l0w1-^$z8gnr*W(NeQEK6#X{Gm@6?yu zy^dkMt~Z)@1_a8v!)ypMlgbV_z^_}ic# z@uQnb&S_2)dmsPxmGh4N{J3Mr`QWMF=R9wtwY3~M=Xh7~NE+{Lzc28! zxp%#&ugs*W=autjpPwXYc0PK$@7DhNj^g}6zt+ufFn3zD<42IBSJUiWk|)%c>~=UH z+g4gJE%J)`%F8-@nLS1i4%N54aX++=?|%LS*B9m=yHn&3{;A_qSbt4jHh&@Oz2MIo zlixQBx^`?{vRh_>bo<%%dy-CU-4_K68j^H%_DdLC{~3E^((`%V(^-_O9`*>OPPXAw zI@xFPp`542rrGM6hQT+*7t$b;|$!kNxy@mpjL@$7zx+&)xgajvP(WYO`M$Gilkw18+L!sl+_3XN ztp3a4DK2?Ob!`7Acg~;w(vQXVW~5$%W&EZ!!WX}J?f&m6?wh@dH>>=HSI?4==^~pRJMD>vb|t^h}uoP z-Q2+e^=;DeqKC8}p8Ne#sN;5ZRmEnZIz9KqEjORq1ICuV^(QlLoU?Dxf487~70-ma=#!QCPNvIUA6LJtY1vtRiIp_qIB&8K2~4?@meHLp;qIDS^> z{2`X_(~CMaxqiKvndN_B+3Gou+BPc9F3XBvc9YF}b=AKsonenTd!EXR)t|A6`zzV> z(ZTQV2baD(iMF$OJX8*?E&o{4yL)n&c9Z7z2#H6|*5XGRe(*JJ*l?7wvGu>20xu8$ z*IRt)X%{YKJESpa3aB}3so*{GcIESP2O7SV_!#q8@xO8VWW3}%tAGIq7c1w2>9Yfp zr?tN9k9_m~=Z$ZF`L1tg7TuFyZ@j5q)%UgU|C)(MJ{ac-UP=C)pD%TIu41TYVs*h$ zTOE7f-RVa=wsA8y3nhFiFPg^x(00|ldy7<5vse~$PCU3_Vm*g*UF3rQGxt;+UBU3P z)Bf^al|Z*mG7L{;jizwKdarqK?(yFr)of3EKhy}C`De+Ucl7_QCO@AoA&tlDP~|g+ zn0gTl$?YpyQjUo({BzzUVr@!?R_d0>lU>ZZhbxkF5+C!m6{zqr_p8!f1JVEny4KJVF_;b^~CmUsM-dSsMN5A^g?=3be0^e*4E0+HYJ<6|W zoc;Ei%E6PuO|SXtOBQiCC#ENfNY1+?Uw_$+J+H|rD@6D7iX3rqmxmvWSAOtzuXwaG zIY{n=Y_(g_nb!87jSCefI-5@mocCh-krvB2*2=!Cdv8oIFz0Dr&|V*7wC|i{byww4 z7oEjd3Yeb%^qp7S%`{o+YqmMlx91BcGXKeRIBnN%7Tx9fH1wx?di|9mDVd4Ihxan5 znB+g-JE2U|`>9RB^{p?A14BL~uRHVH^y1U{olcJC@*KrGXZ{xcsb1K=WwZVtEv@DK z`tR!WSrxrMZ~lMk>---93qDMrRk!b+c4a~KC;zz)XD0U9gvCh5+MV)IdSu(XYx#@f zmd_C`Y}uO0yXHzgsN-HCZ}vE+{^iX*vl(9V9eq{d9xAUjbL)h8h70AocSPP2%-P&g zQEVMpdHKJSr`Fb6M~i9~KRV?6RrbJ&#C(10mro95KX&EHS#iW-w=Ub4H5dIHoF3*p zc=Yyay^>IQG1rbq^Y46_rE#HR)~VI;JDnO&TEEd>-6PwdVYhJgKd3RpnYQS!k)^{VXvl zXV0rslV08tkCRyv`mIdrrRJJ+KknQIEobC|CrMU6KRA8w2VLeB@7wfGF;+bEuQX(I z>2})Y^?8Tuf9CqHF()l|-iyl?o8z2WShLHNLw!ZvZY}%k-{;JpCRuuNaniQFS^d+# zWnAW$)nS*K^Y-h>3)~rsoU%%3Dk+TN4tJ%`itJ^$kSjGweu-*e!ZrPzZ@pm-8AqOE zJ>#9YqxH0RbXNGi2ip5ypPsE0yXppe%cYMY0cT1&)1S(6EO5;FTwl^C@6NT*sp4^_-oz|J{*Yb39`4w7%BPA3l+;(&8+XZQfA}sooA=(f zX#QaOeVd5aD#_QOd-G?mSJC&V7yg+z`CZka<7=08oA0T4wpzcrcw>U87TenPPkSOB z-*o8okl&h9rP{r3Lo|=q@fO#JoZ_Qq=WA-IjX>#kXazgr1W(U*%vuH(2h7Z~ej^ zLDTgqF{krHj9)eVTsq;E!$aQ{qR*c`+i{@#TG#3zKfOIHS7!QZsN6rU9kICJYUP?A zK8imYI@cyHKmFXfR?dfg_PIxOJC|kt+4@X!|Jf}8^=cp7nOWbo z?KjVPvc8nf*QLv;ihqK%sJz|vt%?^WzX}arS?}(5K>658FG-Vgm$$TvUY*yxG^F~$ zgLUVYKR;{5Ri+-_oO5yJl6^Z?$L4h@_dA}sa>mG%GxVv&p65^At~}A59-OBCUmnZK!_DC{iZPS`#XI!UWE=Xnj6u0N4fassoPPt<1=S%uL z@o|47)yikQy#8%pZS)exf;-W2>?LMwjBH+BheJx+igjJ?wsJh^@b3TgXm{0Xhs?T< zTejJ53(n5{+!Wks^14j*>U#y8gZl{|}#bXu5rL{$Ow-d-e$BmrnRm?sUgHDj|9QWCiRJ-D!`$8V_x)Q1-+Numijv8g{Pj-yzALhgA9){JUHt8Rqx*rz zz{47*qBL)F&KDs<La|HOpNO=;3K(kJSawT|V4-uQXrscPb5E~yxSjMq)KUpL>#KE$)Pj=xFhSv;du z-^a>u1Bt`za&sD=^JuP|%=oxcWqYvOrWL1)>X*%vnQWJMxIVF&>*vY|Nq<9mwB3}H ze!f$U+}me%%=*&P;9kCOlj}_*q;%Y#dR%Yi)%x6e^ue4z8Xr;St2;4%$Hbg1*7`qO z`N!95t5Lckrz>tK|5Ru>%7aO zm+z3hp6Bu-v036J^L$;Y=GBvGi~TfG{w)dinX3`J|B-VZ&(RBe}y?Ji?DBy3f!0`+%u(+YeRRWZer*YJ7879%Uw`f#Qn_s(oDSiJ7k z?yG9bU%EOM`L!}ny}ssj>0dw2yaVUW!);F%Wj2pTj0RJFj>Gn KoHYP6=?DPRFQ{Aq From 1405f59d4cd3fc2eaeb07dbbfa5990f0faafb8c7 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Thu, 22 Feb 2024 12:23:06 +0200 Subject: [PATCH 081/176] QmlDesigner: Send only 1 datastore node to collection editor's model Change-Id: I5c8cd7c3e6f15570e71cf46089b56bf8772f47e6 Reviewed-by: Miikka Heikkinen Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Ali Kianian --- .../collectioneditor/collectionsourcemodel.cpp | 18 +++++++++--------- .../collectioneditor/collectionsourcemodel.h | 2 +- .../collectioneditor/collectionview.cpp | 9 +++------ 3 files changed, 13 insertions(+), 16 deletions(-) diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.cpp index 13accfba0e0..7016bf1ef05 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.cpp @@ -196,21 +196,21 @@ QHash CollectionSourceModel::roleNames() const return roles; } -void CollectionSourceModel::setSources(const ModelNodes &sources) +void CollectionSourceModel::setSource(const ModelNode &source) { beginResetModel(); - m_collectionSources = sources; + m_collectionSources = {source}; m_sourceIndexHash.clear(); m_collectionList.clear(); - int i = -1; - for (const ModelNode &collectionSource : sources) { - m_sourceIndexHash.insert(collectionSource.internalId(), ++i); - auto loadedCollection = loadCollection(collectionSource); - m_collectionList.append(loadedCollection); + // TODO: change m_collectionSources to only contain 1 source node + m_sourceIndexHash.insert(source.internalId(), 0); + + auto loadedCollection = loadCollection(source); + m_collectionList.append(loadedCollection); + + registerCollectionList(loadedCollection); - registerCollectionList(loadedCollection); - } updateEmpty(); endResetModel(); diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.h b/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.h index 1909b77c9ab..d0d77775e50 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.h @@ -45,7 +45,7 @@ public: virtual QHash roleNames() const override; - void setSources(const ModelNodes &sources); + void setSource(const ModelNode &source); void removeSource(const ModelNode &node); int sourceIndex(const ModelNode &node) const; void addSource(const ModelNode &node); diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp index df4fee7a7ee..00021ce639e 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp @@ -376,13 +376,10 @@ void CollectionView::refreshModel() if (!model()) return; - // Load Model Groups - ModelNodes collectionSourceNodes; + ModelNode dataStore = m_dataStore->modelNode(); + QTC_ASSERT(dataStore, return); - if (ModelNode dataStore = m_dataStore->modelNode()) - collectionSourceNodes << dataStore; - - m_widget->sourceModel()->setSources(collectionSourceNodes); + m_widget->sourceModel()->setSource(dataStore); } NodeMetaInfo CollectionView::jsonCollectionMetaInfo() const From c1662c83cf67532a3ed11d3e43bf22664755f0e8 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Thu, 22 Feb 2024 13:05:06 +0200 Subject: [PATCH 082/176] QmlDesigner: remove CSVCOLLECTIONMODEL_TYPENAME from model editor Change-Id: I9e1b7979c71ee366a06b56779fbaaad595162417 Reviewed-by: Miikka Heikkinen Reviewed-by: Ali Kianian --- .../collectioneditorconstants.h | 3 +-- .../collectioneditor/collectioneditorutils.cpp | 6 ------ .../collectioneditor/collectionsourcemodel.cpp | 17 +++-------------- .../collectioneditor/collectionview.cpp | 12 ++---------- .../collectioneditor/collectionview.h | 1 - 5 files changed, 6 insertions(+), 33 deletions(-) diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorconstants.h b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorconstants.h index a591719d870..76524762ede 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorconstants.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorconstants.h @@ -5,7 +5,7 @@ namespace QmlDesigner::CollectionEditorConstants { -enum class SourceFormat { Unknown, Json, Csv }; +enum class SourceFormat { Unknown, Json }; inline constexpr char SOURCEFILE_PROPERTY[] = "source"; inline constexpr char ALLMODELS_PROPERTY[] = "allModels"; @@ -13,7 +13,6 @@ inline constexpr char JSONCHILDMODELNAME_PROPERTY[] = "modelName"; inline constexpr char COLLECTIONMODEL_IMPORT[] = "QtQuick.Studio.Utils"; inline constexpr char JSONCOLLECTIONMODEL_TYPENAME[] = "QtQuick.Studio.Utils.JsonListModel"; -inline constexpr char CSVCOLLECTIONMODEL_TYPENAME[] = "QtQuick.Studio.Utils.CsvTableModel"; inline constexpr char JSONCOLLECTIONCHILDMODEL_TYPENAME[] = "QtQuick.Studio.Utils.ChildListModel"; inline constexpr char JSONBACKEND_TYPENAME[] = "JsonData"; diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp index 777de1c3357..ecc2544bcc3 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp @@ -182,9 +182,6 @@ CollectionEditorConstants::SourceFormat getSourceCollectionFormat(const ModelNod if (node.type() == CollectionEditorConstants::JSONCOLLECTIONMODEL_TYPENAME) return CollectionEditorConstants::SourceFormat::Json; - if (node.type() == CollectionEditorConstants::CSVCOLLECTIONMODEL_TYPENAME) - return CollectionEditorConstants::SourceFormat::Csv; - return CollectionEditorConstants::SourceFormat::Unknown; } @@ -194,9 +191,6 @@ QString getSourceCollectionType(const ModelNode &node) if (node.type() == CollectionEditorConstants::JSONCOLLECTIONMODEL_TYPENAME) return "json"; - if (node.type() == CollectionEditorConstants::CSVCOLLECTIONMODEL_TYPENAME) - return "csv"; - return {}; } diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.cpp index 7016bf1ef05..3ae7bafb632 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.cpp @@ -58,12 +58,8 @@ QSharedPointer loadCollection( const QJsonObject sourceObject = document.object(); collectionsList->resetModelData(sourceObject.toVariantMap().keys()); } - } else if (sourceNode.type() == CSVCOLLECTIONMODEL_TYPENAME) { - QmlDesigner::VariantProperty collectionNameProperty = sourceNode.variantProperty( - "objectName"); - setupCollectionList(); - collectionsList->resetModelData({collectionNameProperty.value().toString()}); } + return collectionsList; } @@ -470,11 +466,7 @@ void CollectionSourceModel::onCollectionNameChanged(CollectionListModel *collect return; } - if (node.type() == CollectionEditorConstants::CSVCOLLECTIONMODEL_TYPENAME) { - if (!setData(nodeIndex, newName, NameRole)) - emitRenameWarning(tr("Can't rename the node")); - return; - } else if (node.type() != CollectionEditorConstants::JSONCOLLECTIONMODEL_TYPENAME) { + if (node.type() != CollectionEditorConstants::JSONCOLLECTIONMODEL_TYPENAME) { emitRenameWarning(tr("Invalid node type")); return; } @@ -567,10 +559,7 @@ void CollectionSourceModel::onCollectionsRemoved(CollectionListModel *collection return; } - if (node.type() == CollectionEditorConstants::CSVCOLLECTIONMODEL_TYPENAME) { - removeSource(node); - return; - } else if (node.type() != CollectionEditorConstants::JSONCOLLECTIONMODEL_TYPENAME) { + if (node.type() != CollectionEditorConstants::JSONCOLLECTIONMODEL_TYPENAME) { emitDeleteWarning(tr("Invalid node type")); return; } diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp index 00021ce639e..77997f2dd4f 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp @@ -34,8 +34,7 @@ namespace { inline bool isStudioCollectionModel(const QmlDesigner::ModelNode &node) { using namespace QmlDesigner::CollectionEditorConstants; - return node.metaInfo().typeName() == JSONCOLLECTIONMODEL_TYPENAME - || node.metaInfo().typeName() == CSVCOLLECTIONMODEL_TYPENAME; + return node.metaInfo().typeName() == JSONCOLLECTIONMODEL_TYPENAME; } inline void setVariantPropertyValue(const QmlDesigner::ModelNode &node, @@ -240,9 +239,7 @@ void CollectionView::addResource(const QUrl &url, const QString &name, const QSt sourceAddress = url.toString(); } - const NodeMetaInfo resourceMetaInfo = type.compare("json", Qt::CaseInsensitive) == 0 - ? jsonCollectionMetaInfo() - : csvCollectionMetaInfo(); + const NodeMetaInfo resourceMetaInfo = jsonCollectionMetaInfo(); ModelNode resourceNode = createModelNode(resourceMetaInfo.typeName(), resourceMetaInfo.majorVersion(), resourceMetaInfo.minorVersion()); @@ -387,11 +384,6 @@ NodeMetaInfo CollectionView::jsonCollectionMetaInfo() const return model()->metaInfo(CollectionEditorConstants::JSONCOLLECTIONMODEL_TYPENAME); } -NodeMetaInfo CollectionView::csvCollectionMetaInfo() const -{ - return model()->metaInfo(CollectionEditorConstants::CSVCOLLECTIONMODEL_TYPENAME); -} - void CollectionView::ensureStudioModelImport() { executeInTransaction(__FUNCTION__, [&] { diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionview.h b/src/plugins/qmldesigner/components/collectioneditor/collectionview.h index 649b37f3beb..9f24a856d51 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionview.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionview.h @@ -67,7 +67,6 @@ public: private: void refreshModel(); NodeMetaInfo jsonCollectionMetaInfo() const; - NodeMetaInfo csvCollectionMetaInfo() const; void ensureStudioModelImport(); void onItemLibraryNodeCreated(const ModelNode &node); void onDocumentUpdated(const QSharedPointer &doc); From e8975d6dc095eb3976347c95028d03c3d3f8cec0 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Thu, 22 Feb 2024 15:02:16 +0200 Subject: [PATCH 083/176] QmlDesigner: Fix collection editor model list empty sometimes Also few tweaks Change-Id: I6041c0b37aa5c576e0c4efc988ed15eae02d767f Reviewed-by: Miikka Heikkinen Reviewed-by: Ali Kianian --- .../components/collectioneditor/collectionview.cpp | 13 ++++++------- .../components/collectioneditor/collectionview.h | 2 +- .../collectioneditor/collectionwidget.cpp | 4 ++-- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp index 77997f2dd4f..8de665eeaa6 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp @@ -25,9 +25,8 @@ #include #include -#include -#include #include +#include namespace { @@ -68,7 +67,6 @@ CollectionView::CollectionView(ExternalDependenciesInterface &externalDependenci if (m_widget.get()) m_widget->collectionDetailsModel()->removeAllCollections(); }); - resetDataStoreNode(); } bool CollectionView::hasWidget() const @@ -226,9 +224,9 @@ void CollectionView::customNotification(const AbstractView *, m_widget->openCollection(collectionNameFromDataStoreChildren(data.first().toByteArray())); } -void CollectionView::addResource(const QUrl &url, const QString &name, const QString &type) +void CollectionView::addResource(const QUrl &url, const QString &name) { - executeInTransaction(Q_FUNC_INFO, [this, &url, &name, &type]() { + executeInTransaction(Q_FUNC_INFO, [this, &url, &name]() { ensureStudioModelImport(); QString sourceAddress; if (url.isLocalFile()) { @@ -319,7 +317,7 @@ void CollectionView::registerDeclarativeType() void CollectionView::resetDataStoreNode() { - m_dataStore->reloadModel(); + QTimer::singleShot(0, this, [&] { m_dataStore->reloadModel(); }); refreshModel(); } @@ -447,7 +445,8 @@ void DelayedAssignCollectionToItem::checkAndAssign() bool dataStoreFound = false; if (m_collectionView->isDataStoreReady()) { - for (const QmlTypeData &cppTypeData : view->rewriterView()->getQMLTypes()) { + const QList types = view->rewriterView()->getQMLTypes(); + for (const QmlTypeData &cppTypeData : types) { if (cppTypeData.isSingleton && cppTypeData.typeName == "DataStore") dataStoreFound = true; } diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionview.h b/src/plugins/qmldesigner/components/collectioneditor/collectionview.h index 9f24a856d51..ea02273318d 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionview.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionview.h @@ -50,7 +50,7 @@ public: const QList &nodeList, const QList &data) override; - void addResource(const QUrl &url, const QString &name, const QString &type); + void addResource(const QUrl &url, const QString &name); void assignCollectionToNode(const QString &collectionName, const ModelNode &node); void assignCollectionToSelectedNode(const QString &collectionName); diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp index d17cbb5bfd0..5e76bcc0498 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp @@ -153,14 +153,14 @@ bool CollectionWidget::loadJsonFile(const QUrl &url, const QString &collectionNa if (!isJsonFile(url)) return false; - m_view->addResource(url, getPreferredCollectionName(url, collectionName), "json"); + m_view->addResource(url, getPreferredCollectionName(url, collectionName)); return true; } bool CollectionWidget::loadCsvFile(const QUrl &url, const QString &collectionName) { - m_view->addResource(url, getPreferredCollectionName(url, collectionName), "csv"); + m_view->addResource(url, getPreferredCollectionName(url, collectionName)); return true; } From 5ab8fcad89825396dcaf2b2b8bc9d769bda69a54 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Thu, 22 Feb 2024 19:47:04 +0200 Subject: [PATCH 084/176] QmlDesigner: Fix collection editor's context Change-Id: I9aceb9bf3f772f381006aad30eb7addfe3b1bea5 Reviewed-by: Miikka Heikkinen Reviewed-by: Qt CI Patch Build Bot --- .../CollectionView.qml | 5 +++++ .../collectioneditor/collectionview.cpp | 2 ++ .../collectioneditor/collectionwidget.cpp | 7 ++++++- .../collectioneditor/collectionwidget.h | 2 ++ .../componentcore/designeractionmanager.cpp | 2 ++ src/plugins/qmldesigner/qmldesignerplugin.cpp | 2 ++ src/plugins/qmldesigner/shortcutmanager.cpp | 20 ++++++++++--------- src/plugins/qmldesigner/shortcutmanager.h | 1 + 8 files changed, 31 insertions(+), 10 deletions(-) diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml index c4cf2f7f5e5..d5b9b4c43c8 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml @@ -23,6 +23,11 @@ Item { warningDialog.open() } + // called from C++ when using the delete key + function deleteSelectedCollection() { + print("TODO: deleteSelectedCollection") + } + ImportDialog { id: importDialog diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp index 8de665eeaa6..971752c994d 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp @@ -222,6 +222,8 @@ void CollectionView::customNotification(const AbstractView *, onItemLibraryNodeCreated(nodeList.first()); else if (identifier == QLatin1String("open_collection_by_id") && !data.isEmpty()) m_widget->openCollection(collectionNameFromDataStoreChildren(data.first().toByteArray())); + else if (identifier == "delete_selected_collection") + m_widget->deleteSelectedCollection(); } void CollectionView::addResource(const QUrl &url, const QString &name) diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp index 5e76bcc0498..9a935820175 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp @@ -64,7 +64,7 @@ CollectionWidget::CollectionWidget(CollectionView *view) setWindowTitle(tr("Model Editor", "Title of model editor widget")); Core::IContext *icontext = nullptr; - Core::Context context(Constants::C_QMLMATERIALBROWSER); + Core::Context context(Constants::C_QMLCOLLECTIONEDITOR); icontext = new Core::IContext(this); icontext->setContext(context); icontext->setWidget(this); @@ -317,6 +317,11 @@ void CollectionWidget::setTargetNodeSelected(bool selected) emit targetNodeSelectedChanged(m_targetNodeSelected); } +void CollectionWidget::deleteSelectedCollection() +{ + QMetaObject::invokeMethod(m_quickWidget->quickWidget()->rootObject(), "deleteSelectedCollection"); +} + QString CollectionWidget::generateUniqueCollectionName(const ModelNode &node, const QString &name) { if (!m_sourceModel->collectionExists(node, name)) diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h index b4a6578580f..e52e3e401e7 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h @@ -49,6 +49,8 @@ public: void warn(const QString &title, const QString &body); void setTargetNodeSelected(bool selected); + void deleteSelectedCollection(); + signals: void targetNodeSelectedChanged(bool); diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp index 8dba5b90a41..eabe612fda3 100644 --- a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp +++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp @@ -118,6 +118,7 @@ void DesignerActionManager::polishActions() const Core::Context qmlDesignerNavigatorContext(Constants::C_QMLNAVIGATOR); Core::Context qmlDesignerMaterialBrowserContext(Constants::C_QMLMATERIALBROWSER); Core::Context qmlDesignerAssetsLibraryContext(Constants::C_QMLASSETSLIBRARY); + Core::Context qmlDesignerCollectionEditorContext(Constants::C_QMLCOLLECTIONEDITOR); Core::Context qmlDesignerUIContext; qmlDesignerUIContext.add(qmlDesignerFormEditorContext); @@ -125,6 +126,7 @@ void DesignerActionManager::polishActions() const qmlDesignerUIContext.add(qmlDesignerNavigatorContext); qmlDesignerUIContext.add(qmlDesignerMaterialBrowserContext); qmlDesignerUIContext.add(qmlDesignerAssetsLibraryContext); + qmlDesignerUIContext.add(qmlDesignerCollectionEditorContext); for (auto *action : actions) { if (!action->menuId().isEmpty()) { diff --git a/src/plugins/qmldesigner/qmldesignerplugin.cpp b/src/plugins/qmldesigner/qmldesignerplugin.cpp index 4e98ab92589..389f6d41cb1 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.cpp +++ b/src/plugins/qmldesigner/qmldesignerplugin.cpp @@ -381,6 +381,7 @@ void QmlDesignerPlugin::integrateIntoQtCreator(QWidget *modeWidget) Core::Context qmlDesignerNavigatorContext(Constants::C_QMLNAVIGATOR); Core::Context qmlDesignerMaterialBrowserContext(Constants::C_QMLMATERIALBROWSER); Core::Context qmlDesignerAssetsLibraryContext(Constants::C_QMLASSETSLIBRARY); + Core::Context qmlDesignerCollectionEditorContext(Constants::C_QMLCOLLECTIONEDITOR); context->context().add(qmlDesignerMainContext); context->context().add(qmlDesignerFormEditorContext); @@ -388,6 +389,7 @@ void QmlDesignerPlugin::integrateIntoQtCreator(QWidget *modeWidget) context->context().add(qmlDesignerNavigatorContext); context->context().add(qmlDesignerMaterialBrowserContext); context->context().add(qmlDesignerAssetsLibraryContext); + context->context().add(qmlDesignerCollectionEditorContext); context->context().add(ProjectExplorer::Constants::QMLJS_LANGUAGE_ID); d->shortCutManager.registerActions(qmlDesignerMainContext, qmlDesignerFormEditorContext, diff --git a/src/plugins/qmldesigner/shortcutmanager.cpp b/src/plugins/qmldesigner/shortcutmanager.cpp index b2bd235fdff..961aa1c9520 100644 --- a/src/plugins/qmldesigner/shortcutmanager.cpp +++ b/src/plugins/qmldesigner/shortcutmanager.cpp @@ -231,10 +231,12 @@ void ShortCutManager::registerActions(const Core::Context &qmlDesignerMainContex connect(Core::ICore::instance(), &Core::ICore::contextChanged, this, [&](const Core::Context &context) { isMatBrowserActive = context.contains(Constants::C_QMLMATERIALBROWSER); isAssetsLibraryActive = context.contains(Constants::C_QMLASSETSLIBRARY); + isCollectionEditorActive = context.contains(Constants::C_QMLCOLLECTIONEDITOR); if (!context.contains(Constants::C_QMLFORMEDITOR) && !context.contains(Constants::C_QMLEDITOR3D) && !context.contains(Constants::C_QMLNAVIGATOR)) { - m_deleteAction.setEnabled(isMatBrowserActive || isAssetsLibraryActive); + m_deleteAction.setEnabled(isMatBrowserActive || isAssetsLibraryActive + || isCollectionEditorActive); m_cutAction.setEnabled(false); m_copyAction.setEnabled(false); m_pasteAction.setEnabled(false); @@ -286,15 +288,15 @@ void ShortCutManager::redo() void ShortCutManager::deleteSelected() { - if (isMatBrowserActive) { - DesignerActionManager &designerActionManager = QmlDesignerPlugin::instance()->viewManager().designerActionManager(); - designerActionManager.view()->emitCustomNotification("delete_selected_material"); - } else if (isAssetsLibraryActive) { - DesignerActionManager &designerActionManager = QmlDesignerPlugin::instance()->viewManager().designerActionManager(); - designerActionManager.view()->emitCustomNotification("delete_selected_assets"); - } else if (currentDesignDocument()) { + auto &actionManager = QmlDesignerPlugin::instance()->viewManager().designerActionManager(); + if (isMatBrowserActive) + actionManager.view()->emitCustomNotification("delete_selected_material"); + else if (isAssetsLibraryActive) + actionManager.view()->emitCustomNotification("delete_selected_assets"); + else if (isCollectionEditorActive) + actionManager.view()->emitCustomNotification("delete_selected_collection"); + else if (currentDesignDocument()) currentDesignDocument()->deleteSelected(); - } } void ShortCutManager::cutSelected() diff --git a/src/plugins/qmldesigner/shortcutmanager.h b/src/plugins/qmldesigner/shortcutmanager.h index 70b019217ca..8714bb5fbcd 100644 --- a/src/plugins/qmldesigner/shortcutmanager.h +++ b/src/plugins/qmldesigner/shortcutmanager.h @@ -64,6 +64,7 @@ private: bool isMatBrowserActive = false; bool isAssetsLibraryActive = false; + bool isCollectionEditorActive = false; }; } // namespace QmlDesigner From 3d16642dfddde949c4ed3966fc4b5cc6f70779b8 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Fri, 23 Feb 2024 14:49:00 +0200 Subject: [PATCH 085/176] QmlDesigner: Fix onIsHorizontalChanged warning Change-Id: Ic39ed41e8276209dddf852118f0c62d8c4a1d81b Reviewed-by: Ali Kianian Reviewed-by: Miikka Heikkinen --- .../collectionEditorQmlSource/CollectionDetailsView.qml | 9 ++++----- .../collectionEditorQmlSource/CollectionView.qml | 4 ++++ 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml index bbfcbebdbd2..1b72c0db7cd 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml @@ -20,6 +20,10 @@ Rectangle { implicitHeight: 400 color: StudioTheme.Values.themeControlBackground + function closeDialogs() { + editPropertyDialog.close() + } + Column { id: topRow @@ -504,11 +508,6 @@ Rectangle { model: root.model } - Connections { - target: root.parent - function onIsHorizontalChanged() { editPropertyDialog.close() } - } - StudioControls.Dialog { id: deleteColumnDialog diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml index d5b9b4c43c8..2ea12efe742 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml @@ -69,6 +69,8 @@ Item { orientation: width >= 500 ? Qt.Horizontal : Qt.Vertical anchors.fill: parent + onOrientationChanged: detailsView.closeDialogs() + handle: Item { id: handleDelegate @@ -197,6 +199,8 @@ Item { } CollectionDetailsView { + id: detailsView + model: root.collectionDetailsModel backend: root.model sortedModel: root.collectionDetailsSortFilterModel From fb27084f3828f1b50386fa5a400885ec454bca54 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Thu, 22 Feb 2024 17:43:58 +0200 Subject: [PATCH 086/176] EffectComposer: Generate property specifics for effects With proper specifics sheet, the effect will show the available properties nicely in property view. Also introduced a new concept of FooBarSpecificsDynamic.qml to provide property specifics. The difference to regular FooBarSpecifics.qml file is that the dynamic version will be reloaded always, which allows us to change it at runtime. Fixes: QDS-11995 Change-Id: I744124182428c5c4607856e7970700d9a5e9972e Reviewed-by: Mahmoud Badri Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Thomas Hartmann --- .../effectcomposer/effectcomposermodel.cpp | 188 ++++++++++++++++-- .../effectcomposer/effectcomposermodel.h | 2 + src/plugins/effectcomposer/uniform.cpp | 169 ++++++++++++++++ src/plugins/effectcomposer/uniform.h | 1 + .../propertyeditorqmlbackend.cpp | 9 + 5 files changed, 352 insertions(+), 17 deletions(-) diff --git a/src/plugins/effectcomposer/effectcomposermodel.cpp b/src/plugins/effectcomposer/effectcomposermodel.cpp index be64cfceead..1324493de8f 100644 --- a/src/plugins/effectcomposer/effectcomposermodel.cpp +++ b/src/plugins/effectcomposer/effectcomposermodel.cpp @@ -569,16 +569,154 @@ QJsonObject nodeToJson(const CompositionNode &node) return nodeObject; } +QString EffectComposerModel::getGeneratedMessage() const +{ + QString s; + + QString header { +R"( +// Created with Qt Design Studio (version %1), %2 +// Do not manually edit this file, it will be overwritten if effect is modified in Qt Design Studio. +)" + }; + + s += header.arg(qApp->applicationVersion(), QDateTime::currentDateTime().toString()); + return s; +} + +QString EffectComposerModel::getDesignerSpecifics(const QString &name) const +{ + QString s; + + s += getGeneratedMessage(); + + s += +R"( +import QtQuick +import QtQuick.Layouts +import HelperWidgets +import StudioTheme as StudioTheme + +Column { +)"; + + if (m_shaderFeatures.enabled(ShaderFeatures::Time) + || m_shaderFeatures.enabled(ShaderFeatures::Frame)) { + QString animSec = +R"( + Section { + caption: "%1" + width: parent.width + + SectionLayout { + PropertyLabel { + text: "%2" + tooltip: "%3" + } + + SecondColumnLayout { + CheckBox { + text: backendValues.timeRunning.valueToString + backendValue: backendValues.timeRunning + implicitWidth: StudioTheme.Values.twoControlColumnWidth + + StudioTheme.Values.actionIndicatorWidth + } + ExpandingSpacer {} + } +)"; + s += animSec.arg(tr("Animation"), tr("Running"), tr("Set this property to animate the effect.")); + + if (m_shaderFeatures.enabled(ShaderFeatures::Time)) { + QString timeProp = +R"( + PropertyLabel { + text: "%1" + tooltip: "%2" + } + + SecondColumnLayout { + SpinBox { + minimumValue: 0 + maximumValue: 9999999 + decimals: 2 + stepSize: .01 + backendValue: backendValues.animatedTime + implicitWidth: StudioTheme.Values.singleControlColumnWidth + + StudioTheme.Values.actionIndicatorWidth + } + ExpandingSpacer {} + } +)"; + s += timeProp.arg(tr("Time"), tr("This property allows explicit control of current animation time.")); + } + + if (m_shaderFeatures.enabled(ShaderFeatures::Frame)) { + QString frameProp = +R"( + PropertyLabel { + text: "%1" + tooltip: "%2" + } + + SecondColumnLayout { + SpinBox { + minimumValue: 0 + maximumValue: 99999999 + decimals: 0 + stepSize: 1 + backendValue: backendValues.animatedFrame + implicitWidth: StudioTheme.Values.singleControlColumnWidth + + StudioTheme.Values.actionIndicatorWidth + } + ExpandingSpacer {} + } +)"; + s += frameProp.arg(tr("Frame"), tr("This property allows explicit control of current animation frame.")); + } + s += " }\n"; + s += " }\n"; + } + + for (const auto &node : std::as_const(m_nodes)) { + const QList uniforms = static_cast( + node->uniformsModel())->uniforms(); + QString secStr = +R"( + Section { + caption: "%1" + width: parent.width + + SectionLayout { +)"; + secStr = secStr.arg(node->name()); + + const QString oldSecStr = secStr; + + for (Uniform *uniform : uniforms) + secStr += uniform->getDesignerSpecifics(); + + // Only add the section if it has actual content + if (oldSecStr != secStr) { + secStr += " }\n"; + secStr += " }\n"; + s += secStr; + } + } + + s += "}\n"; + + return s; +} + QString EffectComposerModel::getQmlEffectString() { QString s; - // _isEffectItem is type var to hide it from property view - QString header{ -R"( -// Created with Qt Design Studio (version %1), %2 -// Do not manually edit this file, it will be overwritten if effect is modified in Qt Design Studio. + s += getGeneratedMessage(); + // _isEffectItem is type var to hide it from property view + QString header { +R"( import QtQuick Item { @@ -588,14 +726,14 @@ Item { visible: true // This is an internal property used by tooling to identify effect items. Do not modify. - property var _isEffectItem + property bool _isEffectItem // This is an internal property used to manage the effect. Do not modify. property Item _oldParent: null )" }; - s += header.arg(qApp->applicationVersion(), QDateTime::currentDateTime().toString()); + 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"; @@ -849,12 +987,10 @@ void EffectComposerModel::saveResources(const QString &name) QStringList newFileNames; // Create effect folder if not created - if (!effectPath.exists()) { - QDir effectDir(effectsResDir.toString()); - effectDir.mkdir(name); - } else { + if (!effectPath.exists()) + effectPath.createDir(); + else oldFiles = effectPath.dirEntries(QDir::Files); - } // Create effect qmldir newFileNames.append(qmldirFileName); @@ -871,6 +1007,19 @@ void EffectComposerModel::saveResources(const QString &name) qmldirPath.writeFileContents(qmldirContent.toUtf8()); } + // Create designer folder if not created + const Utils::FilePath designerPath = effectPath.pathAppended("designer"); + if (!designerPath.exists()) + designerPath.createDir(); + + // Create designer property sheet + // Since this is in subdir, no need to add it to newFileNames + QString specContent = getDesignerSpecifics(name); + QString specFileName("%1SpecificsDynamic.qml"); + specFileName = specFileName.arg(name); + Utils::FilePath specPath = designerPath.resolvePath(specFileName); + specPath.writeFileContents(specContent.toUtf8()); + // Create the qml file QString qmlComponentString = getQmlEffectString(); QStringList qmlStringList = qmlComponentString.split('\n'); @@ -946,6 +1095,7 @@ void EffectComposerModel::saveResources(const QString &name) } // Delete old content that was not overwritten + // We ignore subdirectories, as currently subdirs only contain fixed content for (const Utils::FilePath &oldFile : oldFiles) { if (!newFileNames.contains(oldFile.fileName())) oldFile.removeFile(); @@ -1580,17 +1730,21 @@ QString EffectComposerModel::getQmlImagesString(bool localFiles) QString imagePath = uniform->value().toString(); // For preview, generate image element even if path is empty, as changing uniform values // will not trigger qml code regeneration - if (localFiles && imagePath.isEmpty()) - continue; + if (localFiles) { + if (imagePath.isEmpty()) + continue; + QFileInfo fi(imagePath); + imagePath = fi.fileName(); + imagesString += QString(" property url %1Url: \"%2\"\n") + .arg(uniform->name(), imagePath); + } imagesString += " Image {\n"; QString simplifiedName = getImageElementName(*uniform, localFiles); imagesString += QString(" id: %1\n").arg(simplifiedName); imagesString += " anchors.fill: parent\n"; // File paths are absolute, return as local when requested if (localFiles) { - QFileInfo fi(imagePath); - imagePath = fi.fileName(); - imagesString += QString(" source: \"%1\"\n").arg(imagePath); + imagesString += QString(" source: rootItem.%1Url\n").arg(uniform->name()); } else { imagesString += QString(" source: g_propertyData.%1\n").arg(uniform->name()); diff --git a/src/plugins/effectcomposer/effectcomposermodel.h b/src/plugins/effectcomposer/effectcomposermodel.h index 871d0c72980..f851fc3dddc 100644 --- a/src/plugins/effectcomposer/effectcomposermodel.h +++ b/src/plugins/effectcomposer/effectcomposermodel.h @@ -172,6 +172,8 @@ private: QString getQmlImagesString(bool localFiles); QString getQmlComponentString(bool localFiles); + QString getGeneratedMessage() const; + QString getDesignerSpecifics(const QString &name) const; void connectCompositionNode(CompositionNode *node); diff --git a/src/plugins/effectcomposer/uniform.cpp b/src/plugins/effectcomposer/uniform.cpp index 348c7b3a2b0..2fe6bfd2f5f 100644 --- a/src/plugins/effectcomposer/uniform.cpp +++ b/src/plugins/effectcomposer/uniform.cpp @@ -170,6 +170,175 @@ bool Uniform::enableMipmap() const return m_enableMipmap; } +QString Uniform::getDesignerSpecifics() const +{ + QString specs; + + // Uniforms with custom values or define type do not result in exported properties + if (!m_customValue.isEmpty() || m_type == Type::Define) + return specs; + + auto appendVectorSpinbox = [this, &specs](const QString subProp, const QString &label, + float minVal, float maxVal, bool firstCol) { + QString vecSpec = +R"( + SpinBox { + minimumValue: %4 + maximumValue: %5 + decimals: 2 + stepSize: .01 + backendValue: backendValues.%1_%2 + implicitWidth: StudioTheme.Values.twoControlColumnWidth + + StudioTheme.Values.actionIndicatorWidth + } + + Spacer { implicitWidth: StudioTheme.Values.controlLabelGap } + + ControlLabel { + text: "%3" + } +)"; + specs += vecSpec.arg(m_name).arg(subProp).arg(label).arg(minVal).arg(maxVal); + if (firstCol) + specs += " Spacer { implicitWidth: StudioTheme.Values.controlGap }\n"; + }; + + auto appendVectorSeparator = [&specs]() { + specs += +R"( + ExpandingSpacer {} + } + + PropertyLabel {} + + SecondColumnLayout { +)"; + + }; + + specs += +R"( + PropertyLabel { + text: "%1" + tooltip: "%2" + } + + SecondColumnLayout { +)"; + QString desc = m_description; + desc.replace("\n", "\\n"); + desc.replace("\"", "\\\""); + specs = specs.arg(m_displayName, desc); + + switch (m_type) { + case Type::Bool: { + QString typeSpec = +R"( + CheckBox { + text: backendValues.%1.valueToString + backendValue: backendValues.%1 + implicitWidth: StudioTheme.Values.twoControlColumnWidth + + StudioTheme.Values.actionIndicatorWidth + } +)"; + specs += typeSpec.arg(m_name); + break; + } + case Type::Int: { + QString typeSpec = +R"( + SpinBox { + minimumValue: %1 + maximumValue: %2 + decimals: 0 + stepSize: 1 + sliderIndicatorVisible: true + backendValue: backendValues.%3 + implicitWidth: StudioTheme.Values.singleControlColumnWidth + + StudioTheme.Values.actionIndicatorWidth + } +)"; + specs += typeSpec.arg(m_minValue.toString(), m_maxValue.toString(), m_name); + break; + } + case Type::Float: { + QString typeSpec = +R"( + SpinBox { + minimumValue: %1 + maximumValue: %2 + decimals: 2 + stepSize: .01 + sliderIndicatorVisible: true + backendValue: backendValues.%3 + implicitWidth: StudioTheme.Values.singleControlColumnWidth + + StudioTheme.Values.actionIndicatorWidth + } +)"; + specs += typeSpec.arg(m_minValue.toString(), m_maxValue.toString(), m_name); + break; + } + case Type::Vec2: { + QVector2D minVal = m_minValue.value(); + QVector2D maxVal = m_maxValue.value(); + appendVectorSpinbox("x", tr("X"), minVal.x(), maxVal.x(), true); + appendVectorSpinbox("y", tr("Y"), minVal.y(), maxVal.y(), false); + break; + } + case Type::Vec3: { + QVector3D minVal = m_minValue.value(); + QVector3D maxVal = m_maxValue.value(); + appendVectorSpinbox("x", tr("X"), minVal.x(), maxVal.x(), true); + appendVectorSpinbox("y", tr("Y"), minVal.y(), maxVal.y(), false); + appendVectorSeparator(); + appendVectorSpinbox("z", tr("Z"), minVal.z(), maxVal.z(), true); + break; + } + case Type::Vec4: { + QVector4D minVal = m_minValue.value(); + QVector4D maxVal = m_maxValue.value(); + appendVectorSpinbox("x", tr("X"), minVal.x(), maxVal.x(), true); + appendVectorSpinbox("y", tr("Y"), minVal.y(), maxVal.y(), false); + appendVectorSeparator(); + appendVectorSpinbox("z", tr("Z"), minVal.z(), maxVal.z(), true); + appendVectorSpinbox("w", tr("W"), minVal.w(), maxVal.w(), false); + break; + } + case Type::Color: { + QString typeSpec = +R"( + ColorEditor { + backendValue: backendValues.%1 + supportGradient: false + } +)"; + specs += typeSpec.arg(m_name); + break; + } + case Type::Sampler: { + QString typeSpec = +R"( + UrlChooser { + backendValue: backendValues.%1 + } +)"; + specs += typeSpec.arg(m_name + "Url"); + break; + } + case Type::Define: + default: + break; + } + + specs += +R"( + ExpandingSpacer {} + } +)"; + + return specs; +} + // Returns name for image mipmap property. // e.g. "myImage" -> "myImageMipmap". QString Uniform::mipmapPropertyName(const QString &name) const diff --git a/src/plugins/effectcomposer/uniform.h b/src/plugins/effectcomposer/uniform.h index 92e02bc467e..b478b2bed89 100644 --- a/src/plugins/effectcomposer/uniform.h +++ b/src/plugins/effectcomposer/uniform.h @@ -74,6 +74,7 @@ public: void setEnabled(bool newEnabled); bool enableMipmap() const; + QString getDesignerSpecifics() const; static QString stringFromType(Uniform::Type type, bool isShader = false); static Uniform::Type typeFromString(const QString &typeString); diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp index ab988140f59..4e40d9b12cb 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp @@ -623,6 +623,15 @@ QString PropertyEditorQmlBackend::templateGeneration(const NodeMetaInfo &metaTyp const NodeMetaInfo &superType, const QmlObjectNode &node) { + // If we have dynamically generated specifics file for the type, prefer using it + QUrl dynamicUrl = PropertyEditorQmlBackend::getQmlFileUrl( + metaType.typeName() + "SpecificsDynamic", metaType); + + if (checkIfUrlExists(dynamicUrl)) { + Utils::FilePath fp = Utils::FilePath::fromString(fileFromUrl(dynamicUrl)); + return QString::fromUtf8(fp.fileContents().value_or(QByteArray())); + } + if (!templateConfiguration() || !templateConfiguration()->isValid()) return QString(); From 417fbc7861ec172f330d20c0534ca8f85ad44af2 Mon Sep 17 00:00:00 2001 From: Mats Honkamaa Date: Wed, 31 Jan 2024 12:37:05 +0200 Subject: [PATCH 087/176] Doc: Add info about JSON backend function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task-number: QDS-10961 Change-Id: I470fe9e1cda514e9b343d56f3e3bf34b95e3269e Reviewed-by: Mats Honkamaa Reviewed-by: Esa Törmänen Reviewed-by: Leena Miettinen --- .../images/json-new-property.webp | Bin 0 -> 7194 bytes .../images/json-text-binding.webp | Bin 0 -> 8144 bytes .../images/project-jasondata.webp | Bin 0 -> 12292 bytes .../src/qtdesignstudio-toc.qdoc | 1 + .../views/qtquick-connection-editor-json.qdoc | 63 ++++++++++++++++++ .../qtquick-connection-editor-properties.qdoc | 2 +- .../src/views/qtquick-connection-editor.qdoc | 4 ++ .../src/views/qtquick-states.qdoc | 2 +- 8 files changed, 70 insertions(+), 2 deletions(-) create mode 100644 doc/qtdesignstudio/images/json-new-property.webp create mode 100644 doc/qtdesignstudio/images/json-text-binding.webp create mode 100644 doc/qtdesignstudio/images/project-jasondata.webp create mode 100644 doc/qtdesignstudio/src/views/qtquick-connection-editor-json.qdoc diff --git a/doc/qtdesignstudio/images/json-new-property.webp b/doc/qtdesignstudio/images/json-new-property.webp new file mode 100644 index 0000000000000000000000000000000000000000..61c50b57030169f012d64c64cf4b1022476c3f0f GIT binary patch literal 7194 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 Z2QV9~hs7Dp<_kzl4QCW!U}FdX@d0v1237z7 literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/images/json-text-binding.webp b/doc/qtdesignstudio/images/json-text-binding.webp new file mode 100644 index 0000000000000000000000000000000000000000..02dc2ed7a53ebfee5b6a6ecafe52b9c00b67a8cc GIT binary patch literal 8144 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 literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/images/project-jasondata.webp b/doc/qtdesignstudio/images/project-jasondata.webp new file mode 100644 index 0000000000000000000000000000000000000000..ec407d404585570b19b315935a0032a3f9820a5d GIT binary patch literal 12292 zcmWIYbaVTo&%hAw>J$(bU=hK^00Eqg3`vX(VF4Bj9r_Fm6J|5aWz^zgOlGvpHJa+K zqO7E-@p-jD18-+?-orwJ*BiFq-rw*?`@R4Af6l-8-_2jQ@BZE5iE-!W>z?9&Sfg0K z?Elig-|tNS%{R}k%f584OTGEO>%TX?Xa8S+)&IwT4}Q1!AOHXV|5U^CKj;6<|NsB* zKaf9n|9|^b{x5OU>l)<$?Oj*jQ2+n6{fpyY=Rb?z{4e#t&A*+0?|<|E{d~E7&!7F@ z&cCzYE`OtT&i{!Y_n+VYa{stQNRsSB|mfcw2|9|=`?$`0>_TQ|}{BQl+@%h|8^S_sV5n zeM@jyUhnnUXL8oq`8m0HHuqnfa>euUq7bvse`H@t2kiTPinVV0x)nV^QPB)_3yw@% z$@g|kvg$OE#?q;Wj1_})-f-s6PtG*W=|3U(RGj0im9BpK@)_&p@;EQ{M7oM6J?b*h z6&tp7pGvxwWBY#L+dDsAu+83Tu=d_kVeg$ks`#EhuzqMQ z%k+O|)PdQW4FX%%u^koD6PZ)7-ZCiTXjA6B7Jti<@^hVnSzPrCg}au%`VcSpf7%=U zc{`r^ALz;E{9ONamSM;OPPwzEFYp^iT$vkDlkiAM*ne5yvnZC%uc{wwtraZ0-<{_6 zID1oBjdRO9!xWz%%{gDn-R~FcNLZI-O>boH3H6NcWY)<{% zYdE7OvFcY`gHLOlo4DSyk8>(B18?q3niatBv1sz^7cv&J{Y@;Zw63OZ{@)nBMM>A3 z)6Vq2w&D9uEk4_soGj;DnG_6?c)~fCn>M{)b3BE+`e+tcgR=$SieDb$rMtgXyf5>6 za&;rai#Mrjc0bgUc`tdW`jMmJjNQAQeO|jD=j(62##0=83`Equo|mmNpS@hEsOhW!%9oSx*X#|{k6IJ<#Q2l(+*DU} zkGr3FZv0xW_{6?A#&5+>1Fda;q}DCwX};;S^ae-I-RDPGCG|76eyBcoH@yGBo~jw? zZ(Nu+>2W{Vqw`5ALo8CM^7zMH-*&LK=9}KHPiT1g>4#eE-hymiMIAeVN5OkX%!1H?kV^lu1)_Ke(?JT(XTo}`9i@X7S2OG+HS?c$4Ow|&Yc zamw&K*E_wE_5iK@p#lujtC_gWFDNEm)hz$P>`<<%%VhZa?WJJ%>oFD?pS)8a`2V_o zYMI0qTfS|&(-<1XrnIzGO5W{KQ<0D7T-CU3l|-4xrsm6vY)6kaDJ_{JvH9YKF0pbG8dUsalqPy~tly^%GZge|7lXti|kE(*w4BRLKZzjpTgs?B1Do zfk#-JMc!SwSheNFqqg9tqPuo8)EoX3PO&Qdr<~WbJ@iAZJL5!!_MbnKease5EdKHO z-t~7WRtC=2sdf2fKKmKYUwG{3?eS})b@z0>@B40>Z+rFct5DE9@vL~mU*{Ooq$cm| z4qw8yB#+6Or_lG#9d`C7Jc7~uK2>Fp?r>EKeSJ5;%zvJ+fy~Zn)rZ3cO(J8cR z`*~HN_=Bc?;LkG)=NJALi-=eeC1jXa#~*sJUnum)`e$-;9_|i0JUgJOQm*961I3$u z>S4_Wg)jI-TCa6>*4AGUYpp*jw%P3aGyBeNxAS-Wn^Ug)#~f?4JK+K+`SSdqvbDN42Z{IwxX0cin=Er}zEbzI6Iz+s%;$e^uux9ll?f zy^yJYTfd%}o9~yFAA9%D^;G`b{KR{c)84u^WziGQTn=`Ye@&JAv|(z4l6S+Yy&*H! z1QdPL{q{qOUE*iMe7TH8YD*)ZvChsbTm53PePH3bYthr%uN!`t{-Nfe(~0sWd#-Mu z{Htwqnd`I=<|i!c)EB*RS>wSI{MRfy!g=kcshq}@f9>6a!Zy#V=!xIBMClFR#i~Z3 zy(gnGJ#Cohiv0_+vpz91{H}v3^PZ))pI=M6oHyquYsswl@*gU?|E#*L7`lApuCvd>-r4!@*7W=K zK#ajhi^sf4^YVEiR9HiGEb8CKZ z_|$zWNHOq|e$T(*-#6O5lJ@jWQS!oiQ&n*2@2P zJhf@-*=rxTwSQYAXRV)WHT~+g$hmD<#YG8=#G>nV-VXe;@$UKcY?h)=T2$(;#&HC< zx$V&P*0WZzN-&(VNAr@^X;xp?h+lPu8y@YMdTZ+%8K1UqyN{LMv|3!4(e?J_ajlGB zE`6Upl2SQVuF3m+YW^n|H>q`-RpxJfx1csA((TfLSpupq`d9Xrt}Oe$Hj(FLbMl(Y zdtPNtDW7)u(2E$ELzZ@nY&U1I-mY+3Gxe6aj+5QHD)EHlR{WOV`2tr~#Hdbe>#UT~ zn0Qma`Ii2S>L^|H?JM2&Bz3g&*!KM2dpK6GKTyClZuUCMOA9s1W^2~O9GqL)6 zbI+lpPm&v^Dhf+>SZ`gnmtW&=-j{DYJNFsxPF<9{U#n1sbJFY?>cQ)?t_K;Ot}~YH z%>2-hS(nbMSiY`1K`(Vp@jL0#m;ZzpCC-~K!g~7erpt#VSN>e^!9mzw&bl&u+wb|( z`Ml!#H)iQxGzd4l!ua97%0A&(MK33JhVMripRQstR4L0?5mzL)v?IG%+wk#n9UXZu z2}}R=Y3GXC#Pk>!S?pwvdc*Vabiw%sh8?Y|?#~pOzxC}clfBBUr;c@=6Ebm|F+cud z^O2c%4Yw`b^PMwPF?h@R%eil^7u;Jsag+Vi`G@ykVHFQveaW_Pdz2Eto8z1u{+qF^ z@x70;x8+DQZ|E@3RoZ0KVSoFS><^w7fxFjtX{<1|`Szd#MfHSM~>5nr}Fd0%@;`uF5LQvVhin7J-6ygaiuW-G7r z+yC+9$BP`y^=y;xy*d0W`))m268*I@h3UlvuCFO54_&?t8h8W?yohe|}HspWyYya7~-6?)%6~XV*0bY}vYmN#3p{ zreu>E&+Kzyb^IUf!e6k;v&rj)C1`({{^guv?3CjQe{5}kJ9vFF4=R0p_|WUx{SUwF zZc{2)%<$W|?O$+Nbh?q{US79TJy{z(Wi0I%&D+-fW)D+>V&JN-_0l$$5>vdh@3>65 zbgiQzy8Bm|K)|u@ayD^`nvD7`>SsMal6u(F{QjwLE8k3dKJm4C{=-!}?+LOot@;1L zY0e5?gQoplZsmd3naaie zOlGiH`Ke|5eff3Em(PB5FIh~l{-n8q?5no_&$p&I2~CXaf9Uwlbi3TY_0{bgo?4q* zU+?%{DJ(bRGw|)K8?f)%9<97*uR#u97>TQ#+7d2(~ z%bkCw^i}8Fk#W{}cm76He)Cb|X%dVM8#5k;i)@Hka43J*qzKEIwShPPX(g*Jw-GsT z{fY4!X@#kCGIK7i*IYT}NVnv(gadM{$DJaeDvPSzwyt%!< z;GZ25i|iTS-wdB{Eym5=>_TnJilBQ_i%-i6R|wx;8aOXBpVSWEtV zU*|UOrb{g=^M)to-qH1EyAuypke;`n@3-NeHf!q@#pSA)#*p4M99wkVD2F9xuI5}@6-hb zr70(Ft>1CNAhCo&Z%T4!{Xa8l_M3f&txnDT{eA*Z*2U;sGm1F;s^wj89=)_qs(9T@ zIaS;KdxwkuIW9c*{6qGqXD#n!7*bX_X4tfzeR0?+qPygOESHDQ7e_hPc-j3%8bR4F zH!rDiXb^Vq>b+TP9kG3VYO&4MJbl5PJHC1R>S_7pv4Y3mZ}oD+Um}+l-;sAMvg7c( zq4=xxX@9^9qtlloUQ|v#9aqSI_Lh4XL(eqzhfJkwLmzP}9bpZVC>M8oGx39t`|0D2 z<#%O+11yq$B|0%(dOYiZTbNPLvl%}&rhWgV^-l75cS%)U7TbjttNlc;`&j)8OOd>J zYu`*&-@eVQ|5-nKEKXIizWBlP{b@bdPoX54x6fVebR)m2z-E^}uheF({me%@ z=bHMyKAZH`ylUFB^K@uf4W+p90_Ho74uD3Q1SotvD2+VQN1%k4j| z*8G;a`PFOtW(VFjsR>pJ3oOj_<+gZ2cJ_Kk^e!-j6lE`zvMj{0VR3A8YMbd98fBYDTmBjpI^F1!EKAE7odp zM^4H9AAGg-{(`4xR0V~+Hr@OD{C!r`x{Dmox0<;By;Bv{{Ui0ex$)!DeXrhZz3Tse z_4-}Mx5;F%$=b#7C)}ECsP^;l*JHn#=GV{Pt9kOhcYkl$&yJJ-T8tmc|Ck>*efwfl z+bfqtYgfNI=r{MhfBZ^L=Et%=7fbeiS{j%ATlX2a{(p-}MxO%hR26wEC%GBkyD2zL z`TSA_F~g+gs`jQi#evcfB>ET{IzlI8TxAP&6L)2A-anb&@fTN)VER_;m#qh`99yO# zsr*E?)P?=p>+5XX*MAnQ@a$TAdDczmyc^1&^Z86)&RsWq$AiyXt}Qm%dz>-S(D(tv zHx?e7^nJnNpH&|hWa;neY<6#ooouC-&HdxWoVVNFi`HFDJ#eRs|DOB#bB0P!^5Tsy zAKEC)QluSFBhcq%B=+*clvi4Iv6CEyl6U82zGL2VG$6s@fOwnG>!+tC27V1T^jLoR zvE|KIzm?u=*rZ1PGE(N)*Y;@NqsTuI;h*oQ@4ug2^5%xkCyyECZ{%)eoUp91{4~2_ zesapjx8IsOxp#JMRjSF|$0lKwJ#*!%vN>Cl7UbNXy*i=h^Q>1t9yacs8&EUzFLT-v zRmYDa5$(^t0^XKhn_ZjZU$E6Pes^-}_f}D3fkuff&kA0zxPPB{@+$}DbvEzyN*6m$ z(kfs4{YQhf-?5F_b%KGX|9$Ckkhr&NTV!Tf&fL|0Ioux>rg?@1t&*Q{MwWXs>(N6S z?^sFh&iucbxv-Hd$w&6;td&2@Gx$P25k2xiuxd1O6e^LTczJV%=Kzs7>{d8baS$aQgvkH6y8Xdd+MbW6+E z_$-#I-5bo$yK-O6>X~u0Haf#|!=L_6ALm~4SxX;Y|M>1+=5~`=zq6iqCm#`c8|D4! z`YX*R%S)`U*FN@Ylgzp6EBE|d;`>R(O_RAyKYe>VcVW5kzt$%wT0~ZhOz5(Z*J3$p zw_Jl`t-GoIv>n@%yVjn1Gh=-N1Ea^IhmHLew4$H}B^wW1lmnqFa|elB>x) z(3k#epRkY9(}oQR;c*dnwRsCyr*j!@>vjC_ntf$N{F{guU8gP{(d|6`K|$A%X+ud> zWNgUJXK}|C?C+o}JXk6n6Fv2r;2q6h zHAVUJdY3WIGT@JU{9gF|<)>BRR>Jo#RxCfad|u9@8{bZIE@WBldr{JUik*k2_Vo!{ zM3aBD>sx$Wax$)I(fo_9Tl*D0hf8dKxTC@QqkJ1fobt7fU%Ej#0^6^(PG^?NoBVLY z{HyDZcHd3dUQ=yodu;SE*|l2NzaPWn`xJKG zn-;!4v+=e;@TCb;E;Iz4bS$^M{5tdg)^o|n8`tr;?Y!HsqQ=-;pu#wD-9;W_`MyPW z_%GgKDYWg}8~WhVzh4vT_`l=|JWz5-Hdv#hn|5i=jCsn^vYVnaQmq2b=I`9IE8^(! zt`na!zRlfQ{Qb)0U1irrmaL3QPhI>|=W|ky&H~AVcY+Wk#COA zdX9;op4jbWoOW=f&zic-Y<;iwy|t~7JCe3pp1 zP@L6Q(4;)Czf7ksEz#kZ)3uMhef3j69<@Ikp#Em=j-8fgKP&SYE_ik$V3(^*$E%xj zGL-H)-(WhjAujHM(~@3pXO=1KFXr80mO1ag#whNPTX*?M0>vv$sQn`^y$ zkIKCl9qux!by|L^2J3D}=b376d(hg#?R9|J?f>ruyIH;NXf8W3EzDKQ@tWK*ucSrW z7(Az{$~)U_3A|=na(8F-@fEvPE==%S+EK`U&BAu&s!5Md&GzG7BLBV4XPVu)8=DSp zo%?Y1(s$WHXV1H31%FAodu)rLX~}aw)mNEGGVu&;Gd}&8s^hq~CT6AF@>%`v>VJ>) zTb^6np7ev`xad^1xm=E49n1rI-=1F3&S!T)c&Yz#xw_-qXMA?KJS{Lw@2yPT4Az+n z_r6Ttw&J&Z$f{7&RrY>S*%H@c9h7F=p25fUbxzb`_9z+gpE*m@b*Eh0)z{12vLXJF zSwu^Is4=JZsqUFUfx9x*B@fx`xbtM@!S!j@hxK1{Pxt8fnH-gW;qI&Dk5b!WA}-7e z|JMBG9sgaWTD1npE}_Lwq?mXd<4*dmcAX#hD8s2tQ&0I>VJ!RMM9x=}zD=y=Vz5&V zd%%1>!pb~l-Tj?CCAL=rGoqflrmk~YxLC<+_DM!=lgn-bD=rBe{nA~wTISA;ZsBm_ zhrj0e{F-4@;br!(BI2(_pq!8T|5wfH6>@9E7BcY3mIvMRN?drVzHIp$|84%+GPizN z#!T)`li0m`@`jfa5|2BuJ#}g>2-*@G+|Zz<;<3}~3B$H$;?7Y@{8vSF9!&M&ejv`i z>-5A=l7fEG_wR3ry4ZhYYl7Jn5pLJ01!_jGlXgg6ol+OP)%D18G}3&8&Ia3{x`YZFhQnUs>F`)4KHSvF***n!-2K7`fm6_xPI4R5GyPVdXseGRvu7)6{5LGgZ=SY#y11eL0iEL4Ypy;C{Q7t{ zV{N5=f@B5TpOPgrbc`=0tk+u+<;?ss?);gvr!?}6yRF3LJpDiY!hQ*D#vML>Nhjh8 z8y%8ga<;j8&HVg)vU8h}(RtXE1Q>&`&=k-rvh|%;&3+!{h^V#=Uw}kqZ&$o6aPB^>Nne|Hiqzzl^ z>k8|B>HoCVc~s;eTe4(pZm?w0xjVll6v_);a2`mxf7arf!i!lCwt9q|jo*AHe4gNe zfYP#qs#~`mI%FKZ)F$(uK)%+xGe*^F|6jdm;#t~%vWDmOn}0$jN!12w^G@yK?$v9J zeQ#5K@7u96aTfCvtQYa~&Ni1fm07j|-LusT%6M84ss*iH4umzoyK z_BCC~Fxx9}FZI{vn2Q8?L@v^=xNT_}jKeg{vlV^-I+}apldtAEa^e`vwP@jJV137W^o_@<+|} zbk_ZKk%!NonE1=T;dzH_Xy4Q0QuF4@^Hg4~?{X=uoU?YDkyn+8!7|rQry1*a_onhL z+GxeLBPUci@Y@$P4K0i?-T8Yf(^)co$CsGg-|R z)^{IuGG9c6-kaXW=eXqA)=%FB?!Ne)vGzqpkv4zp=8mmB91mH1s4XSw%YUqpV7!R&(@awpl{jo%odr&&1f%KNBiWr_>0 zzGvAWTy{udrRW@$F6k$=rYT(W%(MSKZa-Y@^7q&brqVYjCoRZZy5eM*mw_R}lYt)WfZbePhc85}P!=%EF zo08u`?>=ngDl_;+%QEa!8%soh!e!69FE*N>TA)wi}y{2v(P7no?K25;-U{kM-_bFAsD+>!Nqe+2S*16J9+FAmf!{@1IfesA^4YQ3B?&w2T;^x&8D&3-wv zcGkR$S*tgvyS>X@T$QM|@S(`|t!{mcl@~iCbNwsWKPv20%=|U2bV=BUiJUj9bQar8 zVtTJP+h}9vwbs&4v2kTzJC51T)}5XH>A@ZatY}2*wQ~Zl+Q*87%Ylllt<`NL|5xJNA z@NSlL{42+exy4hj8z1}j?9Bd!KC2WxF239mEx+;9uf2=+&k3Bl@~?QVP2&7zJSF@q zU%O9RA@SuK2mj)qb}QYYq~rhiy!LGtnpzT<=B9GvlH%pZ#-;jl+4p8=_dE;|Z*)BP zG3!LtrTKY3e{bKG=p^vSz3acOjjN~k6RiYRJ}1qCY|FPEp0x6%-^!cw3Y8as5KzlY z+dn0&&^FENeOLU3zZY#wgkH3ME7vR9I`5aem&lp!0IvCrD?bMxm9k%wb@s%s7jl1p z7N+kNeCCxsDa&tV_9iWvODPwZ&lbx0@aEk8=-rwv?9XjFr&&$gBf9aR0Q*(9&}5}6 z%J1J^k~R9q`cOLHx!>!L^%lBnMVGQa*^KXCr)&C3@!O$v)k@<%1#Bl$-ixlm?rCa z_NvJK?zj8Yp)3# zoH}n=w_{^g%Z0AJ@ft#=DqPd2J^jD&-M+UsZMoQA8z+Aixj0$Kut_cGrgcK-*|xp@ za#I@mCv$D~JsXgBa!2I+rk$1dZ|@M_x1u_1%6!pt<>7NeL|v7d{ML)zI{b4kYw&^N zCn8>conS6(yj<08Jb1B`;Y99Q+cd5hjaBKdfU)_#fk%_RoF!7hksgHV+ zSK7JP&u21owCFI8KJ$`wUHzF!lWYQAWGh7u#A)PC587I8G%;rB-&qB6`k(&1a8P8` zoNkg-K5^S?tHWmgljm5zyLI~P@5K>nn@(x1n(-*+(~$#$S9V`LeY5AUulmYL9YH;n zkmqyv{ruL|XZnAOlI6>St1l~WpE~6r*LKfmV#Bp5#=MhL_@(!6W!L!hRa7dwR^$K(*Y_W>;ZSGMo3X^|)@N4Lm)rl&mk+pBiFZb`B5MVY zJ?&9GkDorznaF3|pLdDwxXgF%m1ATJ-Xn9Ch5Nyk<| ztKrb*c^gfN^Hj`LWX?@L&t0(V@Rr&O;wFwVYS|Mz()}Bl6EiqxerTM$Lx0`I;JaVb z!@{E`O+U^1F)d%TN3)mOr*dV)ikS)k{N`wLZ1as>)`2d~EiXkJ)V3 z*QOOWn6ZER?;2DU^RG~AVbX{FTss2&Uy1EHc|reiW^QnE-KT`dAFQU@C9YfV93Zg9 zGX0eIlA5Nc8OE#;|1RX$H!hxiN>e1Fb${srgY(jREN=*gXw@YOd7VA|@sDzBw85gO ziL3G>W=vqd>##SY^lu)YjO%y32r1FilUB~m$okKu^2PBjr>Ay9UbN$jmCj4ToxWy& zU@BJU^t-m_w$-_Wub1Bb@7^D?Uz2}vNx64V#Sy3HnICmmdv?xzAanm|^5P}o?i*Eq zy7@$TRyCi^HCERVi21G?e3<(eU+n3XeF=B3o}GR#p zdJJYe*ogP#q^y{E#KKm{MB)7POxLeBIqGAX!sY)iI-3^gD%{i2eOBbJ(2tNa%8ljn zi(aldWwYRbf_d%fn!Fb>*=g@N<$kW3^+Mm;lHqDo=L?PR>a~nFS6FF(|1`8p-T*>jw2 zJ!FFH#P0syt>(Jx=P!wgYY&}@bLmbJi4J~$sH;B2WgffhnMXJ4=NcOJGV~Ni24$Xq zd&K9uueH>sg;kcP_aE$gcae2s`NdbKAGNwGR{fa7>i$8oZNt_BE|YfrXH`>rd^PQP z^VDl9>=DT-K{Gey*V>`B6I~zGeCMwfnh?mqlp5SHDy)wfOeERnPq8*CJg0V0 zurFxpake+XvwozUJshsf#1~jI_f2cZ@w*+pg2v%I-TxWCOiEQ=#4vvQg7db>X5 z8Ob-oYU)oXaqtPh6FhaqBV(CWyp_x9Np}vbW;j)IPAz)*|IL1Zw+8B*DSNXlUfs5T zDslV%pGkHV8?HN+c@{mF-u@uw<-Q0l}Z-(db%NA6j%VFKH$ zkW1S?X9)jYwBXbKucep}X7ccCvxyFTRn_cNbXFoE#PNDe z)Q-8!zUwWNk=JmR-EZ0!#u68QgJ<^pYzGGB=roSG)f!A$PF{I=p%Z`CY!q)$Ol>SQ z3}rW0w_>(>oe`=Xel|bhbe;L!OAEd}Hu7~bwf$RuJ#+UG-lZ2-ObEL7V6XY4#EGW) z`=c-KcH>bNPO;vXrn;tTZIJ@%~oTICuj*WtOe@w!FzasBNu|@PTHWVg zW&LVh^Gx~DRr4%qf5qL4)%@;%W2uv`ztQ?#H0(pqhIW?X(;M%|Up>TFv*?TZ+f}X- zMGIQ`uDU+DlM}q)pEsxc(F-f(^F&nBSrbCs3ibGzT33k(hVY!TKj%LEz00{BinmJY z^*@=}?2Iv-AMfh_eA#l{e_m@8=Q`X}T5|NK?5WgM6OXjd65Y>iJ=N_-rF~72&iTMo z56q%Me+5{3tLEJ1YbtVe@=@yfReLh#oxMUm(?8BhhsAa;QhsbPYyLGI-_^I)u~kjD zbx3hpw6TLB|DyFD>vhy#wKkes@Hg!_&%koUXLHombf(8UGxGNzW^iU%dvfhGf!s-Y zwY%M3d=C8=?9f!06LG9Qp-J%3*;|~w2X8*isS(e+{bYI7vrFQ}28vb9Q;r#*5GeH6 z^4I^<-0$yB9g(lG5_&c{dchAl&YotE#Hhb+kfF@ znbyI;>{HRrqaS+Zh0TXuiaOJJeg8ty;t;kh1ahkL!a@9Hi~Q0^0ndDP~3q;Re2 z43CcOhFc9x-JV@Y|9VW><-s=&?>WLJmY#Xeedmevb%CF^_d9x5-`Xe~%;Xi8k{tB> z!-j4BtG3;7*%4aWdr9kQWxMVLv!)eC+syx`&O8@!DA9HQs!+Y#+Jf3o4w>+*Y7;vr zaJ}Z>oXImzy9z3wTaw@XV!_{|FP_@#ewrYz_Ue~;_<7D#C$8SGQQ2G)?6G{#^9PG; zg={=?q8^`}?v&3eXR^IPF*M-Wl+z^%;ZJWq*roM$pV70M<&W0&_HVAAajA5=>gg>D z^u>5pp8brTB$#!pRwDM^EyrgaOegm3d}+v{C3fk747>C1o6~OjRsLD~?$5vW*)x=y z7ksMO70QtIGJElqJ(mN0E0-^LaLM{!aL^yl(@}qOmfYE6uD^$y{b%K-n7F^Pb(%-F zH>(y!m#98{a?S5@*3%Up4Ib|z(f5X`>{ZaMb6M-?&RS7 zTi23R{N4o?-FTuW%q~`Q#hf*E^UQ}cwO*cWnDIO1=z7lpu{*mWvPDBD?o45m$$x2h zg6G86FI)8eUY?)0%;5A#&yA;BeLvk7>fFD^J6UA?$1+)VCr*h)m(%+Tr+?l)dySNi z^C1BP)q5{{wx%Z=G{;>Q=YB1{L9%71NdJ|;hl?u;WfsoV&xtl%kbLuw`>vy&5A**{ z{=z%?vi91iFBa*ioAk!qN{*SEJn^*_ODNOJPuy=8RWIAHq4Qxr&)W9DGEME<#oSEi zw&aVS*{Z%^$HE<@EDQMWolBEFz_sk;n~p$^Bio%HCN;E0uDl*QTUP9n{mj#L&P*Ov z5@j}m4bMFIZ2Mz+4!+k|WBBo^S}|L7ewTF0BeP>itS)c-cdzj`&-!~OFL6CBo4xbB zt4Pemk1>fl2O92W@aKffXSdreEt6fAQ=nyM+?~Jw?Y2tuel_n7(*rSiVvZ99mfYTV z_3h!Gx<&!qd|qu6V$~ZMKp-I4(bJECfx*Jb)0g=zNQ6OvA%R(lfsug`MuX&7G{AB^ f3=9lR_8-7(upSm?Fq/} folder of the project. + + \section1 Connecting a Text Property to a Data Source + + To connect a text property to a corresponding field in a JSON file: + + \list 1 + \li In the \uicontrol Navigator or \uicontrol 2D view, select a component + that has a text property, for example, a text field. + \li In the \uicontrol Connections view, go to the \uicontrol Bindings + tab. + \li Select \inlineimage {icons/plus.png}. + \li In the first \uicontrol From field, select \uicontrol {DataStore}, and in the second field, + select the JSON entry you want to use. In this example, \uicontrol {backend.name} is + selected. This corresponds to the \e name entry in \c {data.json}. + \li In the \uicontrol To field, ensure that \uicontrol text is selected. + \image json-text-binding.webp + \endlist + + Now, the text field is populated with data from the JSON file. + + \section1 Adding Data Fields to the JSON File + + If you add data fields to the JSON file, you need to manually do the same + updates to \c {JsonData.qml}. + + \list 1 + \li Go to the \uicontrol Projects view and open \c {JsonData.qml}. + \image project-jasondata.webp + \li In the \uicontrol Properties view, create a new local custom property. + \image json-new-property.webp + \li Ensure that the name of the property matches the data entry in the JSON file. + \endlist + +*/ diff --git a/doc/qtdesignstudio/src/views/qtquick-connection-editor-properties.qdoc b/doc/qtdesignstudio/src/views/qtquick-connection-editor-properties.qdoc index 8ecfa8701b9..bc93841b4ef 100644 --- a/doc/qtdesignstudio/src/views/qtquick-connection-editor-properties.qdoc +++ b/doc/qtdesignstudio/src/views/qtquick-connection-editor-properties.qdoc @@ -4,7 +4,7 @@ /*! \page quick-dynamic-properties.html \previouspage quick-property-bindings.html - \nextpage quick-states.html + \nextpage quick-json-data-properties.html \sa {Specifying Component Properties} \title Specifying Custom Properties diff --git a/doc/qtdesignstudio/src/views/qtquick-connection-editor.qdoc b/doc/qtdesignstudio/src/views/qtquick-connection-editor.qdoc index 58831990191..586098ffca8 100644 --- a/doc/qtdesignstudio/src/views/qtquick-connection-editor.qdoc +++ b/doc/qtdesignstudio/src/views/qtquick-connection-editor.qdoc @@ -33,6 +33,10 @@ not otherwise exist for a particular \l{Component Types} {component type} or your custom components. + \li \l{Connecting Properties to JSON Data Source} + + You can add bindings between properties and data from a JSON file. + \endlist For an example of using properties, bindings, and connections to create a diff --git a/doc/qtdesignstudio/src/views/qtquick-states.qdoc b/doc/qtdesignstudio/src/views/qtquick-states.qdoc index b33c54f5625..9f91c52f46f 100644 --- a/doc/qtdesignstudio/src/views/qtquick-states.qdoc +++ b/doc/qtdesignstudio/src/views/qtquick-states.qdoc @@ -3,7 +3,7 @@ /*! \page quick-states.html - \previouspage quick-dynamic-properties.html + \previouspage quick-json-data-properties.html \nextpage creator-live-preview.html \title Working with States From 3f40da097813a35762a8c92dd01cd675656ed96d Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 23 Feb 2024 17:48:29 +0200 Subject: [PATCH 088/176] EffectComposer: Fix qml warnings Change-Id: I40abc68800b95888edae0450625f20d51fd8ed74 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Reviewed-by: Mahmoud Badri --- .../EffectComposer.qml | 10 ++++++---- .../EffectComposerTopBar.qml | 18 ++++++++++++------ 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposer.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposer.qml index af1c16491e0..dda1d93ff96 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposer.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposer.qml @@ -185,7 +185,7 @@ ColumnLayout { style: StudioTheme.Values.viewBarButtonStyle buttonIcon: StudioTheme.Constants.clearList_medium tooltip: qsTr("Remove all effect nodes.") - enabled: !root.backendModel.isEmpty + enabled: root.backendModel ? !root.backendModel.isEmpty : false onClicked: { if (root.backendModel.hasUnsavedChanges) @@ -296,14 +296,16 @@ ColumnLayout { } // ScrollView Text { - text: root.backendModel.isEnabled ? qsTr("Add an effect node to start") - : qsTr("Effect Composer is disabled on MCU projects") + text: root.backendModel ? root.backendModel.isEnabled + ? qsTr("Add an effect node to start") + : qsTr("Effect Composer is disabled on MCU projects") + : "" color: StudioTheme.Values.themeTextColor font.pixelSize: StudioTheme.Values.baseFontSize anchors.centerIn: parent - visible: root.backendModel.isEmpty + visible: root.backendModel ? root.backendModel.isEmpty : false } } // Item } // Column diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerTopBar.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerTopBar.qml index c01bc6dc992..d5ac4461c48 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerTopBar.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerTopBar.qml @@ -28,7 +28,7 @@ Rectangle { style: StudioTheme.Values.viewBarButtonStyle buttonIcon: StudioTheme.Constants.add_medium tooltip: qsTr("Add new composition") - enabled: root.backendModel.isEnabled + enabled: root.backendModel ? root.backendModel.isEnabled : false onClicked: root.addClicked() } @@ -36,8 +36,10 @@ Rectangle { style: StudioTheme.Values.viewBarButtonStyle buttonIcon: StudioTheme.Constants.save_medium tooltip: qsTr("Save current composition") - enabled: root.backendModel.isEnabled && (root.backendModel.hasUnsavedChanges - || root.backendModel.currentComposition === "") + enabled: root.backendModel ? root.backendModel.isEnabled + && (root.backendModel.hasUnsavedChanges + || root.backendModel.currentComposition === "") + : false onClicked: root.saveClicked() } @@ -46,7 +48,8 @@ Rectangle { style: StudioTheme.Values.viewBarButtonStyle buttonIcon: StudioTheme.Constants.saveAs_medium tooltip: qsTr("Save current composition with a new name") - enabled: root.backendModel.isEnabled && !root.backendModel.isEmpty + enabled: root.backendModel ? root.backendModel.isEnabled && !root.backendModel.isEmpty + : false onClicked: root.saveAsClicked() } @@ -55,7 +58,9 @@ Rectangle { style: StudioTheme.Values.viewBarButtonStyle buttonIcon: StudioTheme.Constants.assignTo_medium tooltip: qsTr("Assign current composition to selected item") - enabled: root.backendModel.isEnabled && root.backendModel.currentComposition !== "" + enabled: root.backendModel ? root.backendModel.isEnabled + && root.backendModel.currentComposition !== "" + : false onClicked: root.assignToSelectedClicked() } @@ -63,7 +68,8 @@ Rectangle { Text { - readonly property string compName: root.backendModel.currentComposition + readonly property string compName: root.backendModel ? root.backendModel.currentComposition + : "" text: compName !== "" ? compName : qsTr("Untitled") anchors.centerIn: parent From d576441d972229452407bc4a65debe68b36eb5cb Mon Sep 17 00:00:00 2001 From: Teea Poldsam Date: Tue, 20 Feb 2024 15:45:52 +0200 Subject: [PATCH 089/176] Doc: Update Log In UI - Components Edited the tutorial and added or updated images. Fixes: QDS-11188 Change-Id: I9db7bacd28b9899fcfbf51e2d3447c060c578013 Reviewed-by: Johanna Vanhatapio Reviewed-by: Qt CI Patch Build Bot --- .../doc/images/loginui1-add-assets.webp | Bin 0 -> 17782 bytes .../doc/images/loginui1-add-entry-fields.webp | Bin 0 -> 17704 bytes .../doc/images/loginui1-add-image.webp | Bin 0 -> 13630 bytes .../doc/images/loginui1-add-push-buttons.webp | Bin 0 -> 20726 bytes .../doc/images/loginui1-align-buttons.jpg | Bin 37984 -> 0 bytes .../images/loginui1-align-entry-fields.webp | Bin 0 -> 7038 bytes .../images/loginui1-align-push-buttons.webp | Bin 0 -> 9478 bytes ...utton-background-properties-rectangle.webp | Bin 0 -> 6750 bytes .../loginui1-button-control-properties.webp | Bin 0 -> 7578 bytes .../doc/images/loginui1-button-styled.webp | Bin 0 -> 12716 bytes .../loginui1-button-text-properties.webp | Bin 0 -> 10352 bytes .../examples/doc/images/loginui1-button.png | Bin 36705 -> 0 bytes .../examples/doc/images/loginui1-button.webp | Bin 0 -> 15286 bytes ...tton2-background-properties-character.webp | Bin 0 -> 10328 bytes ...tton2-background-properties-rectangle.webp | Bin 0 -> 7222 bytes .../loginui1-entry-field-properties.webp | Bin 0 -> 8388 bytes .../images/loginui1-entry-field-styled.jpg | Bin 45781 -> 0 bytes .../images/loginui1-entry-field-styled.webp | Bin 0 -> 10894 bytes .../doc/images/loginui1-image-properties.png | Bin 24896 -> 0 bytes .../doc/images/loginui1-library-assets.jpg | Bin 68266 -> 0 bytes .../examples/doc/images/loginui1-library.jpg | Bin 76710 -> 0 bytes .../doc/images/loginui1-main-page.jpg | Bin 60012 -> 0 bytes .../doc/images/loginui1-new-button.webp | Bin 0 -> 10122 bytes .../doc/images/loginui1-new-project.webp | Bin 0 -> 31784 bytes .../images/loginui1-project-main-page.webp | Bin 0 -> 11090 bytes .../examples/doc/images/loginui1-project.png | Bin 35138 -> 0 bytes .../examples/doc/images/loginui1-project.webp | Bin 0 -> 14630 bytes .../doc/images/loginui1-properties-image.webp | Bin 0 -> 10768 bytes .../doc/images/loginui1-properties-text.webp | Bin 0 -> 8996 bytes .../loginui1-push-button-properties.webp | Bin 0 -> 8206 bytes .../examples/doc/images/loginui1-ready.jpg | Bin 89490 -> 0 bytes .../examples/doc/images/loginui1-ready.webp | Bin 0 -> 17428 bytes .../examples/doc/images/loginui1.webp | Bin 0 -> 9370 bytes doc/qtdesignstudio/examples/doc/loginui1.qdoc | 220 +++++++++--------- 34 files changed, 114 insertions(+), 106 deletions(-) create mode 100644 doc/qtdesignstudio/examples/doc/images/loginui1-add-assets.webp create mode 100644 doc/qtdesignstudio/examples/doc/images/loginui1-add-entry-fields.webp create mode 100644 doc/qtdesignstudio/examples/doc/images/loginui1-add-image.webp create mode 100644 doc/qtdesignstudio/examples/doc/images/loginui1-add-push-buttons.webp delete mode 100644 doc/qtdesignstudio/examples/doc/images/loginui1-align-buttons.jpg create mode 100644 doc/qtdesignstudio/examples/doc/images/loginui1-align-entry-fields.webp create mode 100644 doc/qtdesignstudio/examples/doc/images/loginui1-align-push-buttons.webp create mode 100644 doc/qtdesignstudio/examples/doc/images/loginui1-button-background-properties-rectangle.webp create mode 100644 doc/qtdesignstudio/examples/doc/images/loginui1-button-control-properties.webp create mode 100644 doc/qtdesignstudio/examples/doc/images/loginui1-button-styled.webp create mode 100644 doc/qtdesignstudio/examples/doc/images/loginui1-button-text-properties.webp delete mode 100644 doc/qtdesignstudio/examples/doc/images/loginui1-button.png create mode 100644 doc/qtdesignstudio/examples/doc/images/loginui1-button.webp create mode 100644 doc/qtdesignstudio/examples/doc/images/loginui1-button2-background-properties-character.webp create mode 100644 doc/qtdesignstudio/examples/doc/images/loginui1-button2-background-properties-rectangle.webp create mode 100644 doc/qtdesignstudio/examples/doc/images/loginui1-entry-field-properties.webp delete mode 100644 doc/qtdesignstudio/examples/doc/images/loginui1-entry-field-styled.jpg create mode 100644 doc/qtdesignstudio/examples/doc/images/loginui1-entry-field-styled.webp delete mode 100644 doc/qtdesignstudio/examples/doc/images/loginui1-image-properties.png delete mode 100644 doc/qtdesignstudio/examples/doc/images/loginui1-library-assets.jpg delete mode 100644 doc/qtdesignstudio/examples/doc/images/loginui1-library.jpg delete mode 100644 doc/qtdesignstudio/examples/doc/images/loginui1-main-page.jpg create mode 100644 doc/qtdesignstudio/examples/doc/images/loginui1-new-button.webp create mode 100644 doc/qtdesignstudio/examples/doc/images/loginui1-new-project.webp create mode 100644 doc/qtdesignstudio/examples/doc/images/loginui1-project-main-page.webp delete mode 100644 doc/qtdesignstudio/examples/doc/images/loginui1-project.png create mode 100644 doc/qtdesignstudio/examples/doc/images/loginui1-project.webp create mode 100644 doc/qtdesignstudio/examples/doc/images/loginui1-properties-image.webp create mode 100644 doc/qtdesignstudio/examples/doc/images/loginui1-properties-text.webp create mode 100644 doc/qtdesignstudio/examples/doc/images/loginui1-push-button-properties.webp delete mode 100644 doc/qtdesignstudio/examples/doc/images/loginui1-ready.jpg create mode 100644 doc/qtdesignstudio/examples/doc/images/loginui1-ready.webp create mode 100644 doc/qtdesignstudio/examples/doc/images/loginui1.webp diff --git a/doc/qtdesignstudio/examples/doc/images/loginui1-add-assets.webp b/doc/qtdesignstudio/examples/doc/images/loginui1-add-assets.webp new file mode 100644 index 0000000000000000000000000000000000000000..dc205b74dce4225cd51eacb6055d06fbd36b8bbb GIT binary patch literal 17782 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 literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/examples/doc/images/loginui1-add-entry-fields.webp b/doc/qtdesignstudio/examples/doc/images/loginui1-add-entry-fields.webp new file mode 100644 index 0000000000000000000000000000000000000000..f306882cf21daa8d716e44a8a73bb41c54d511e6 GIT binary patch literal 17704 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 literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/examples/doc/images/loginui1-add-image.webp b/doc/qtdesignstudio/examples/doc/images/loginui1-add-image.webp new file mode 100644 index 0000000000000000000000000000000000000000..c3fb7d3051fde17ace955662e064627e559d8686 GIT binary patch literal 13630 zcmWIYbaOK^Wnc(*bqWXzuu#x4Wnj4A!Z?>v>m$<*CcC+Qi+xp8Rxei!+G(*txHCQP z_wUbh_AhuY(0}5&n%x=hmyOS4ic5vp^ggxw{6PjTLR&cAT}`+p7phj|m^ zX8u*L?EAa^?}6{Ve;U50|1-YA{OkIX^i{{d*9X==+<)h9Uj2`{2Q_B@7ya4(?aEh% z|NjqKKi&Uj{$1PpADln?KiK!%E&lQEYyCUW z|D*Fq&VTvqu;1ig=kD}1_1nI%ev^K&|FiwE`u+U*ztsQz zpH2Qy{mF04|8oCs|Cj#5{&f7Z_&5K5*024a{Cnct-Cz2@+~4+}asGMxJ^zkWXV?G# zF8zD+|NnRUzkUAyqjui9*%uqVo^jrsY{n;eiJ_pdWNMY<2^OI#Lf`9l{;P_8RC?&q zzol#B`OI!leW5%dYT{RiKS>sjjN43#EBS)f|1{z}XUhIAEwfUj(^+cm?2@gAte=YP zJ^Y-hEAHXvdR6;|O(kzmhw{nqud5AQ)h=PM?DD_T*RlII?)rCq>+5a)uPTdX+PX|z zas4{G^vB{@hIO46lGOF5_A&fn?F%TX-E@EJ1>OTIL*sc0|LULRlJxm+boWE(Bxm6p z+=@aeIqXp_!n&zTTz4h^_{5(kLB@@b-)Dl+)# zar5Qn;F)5kmeXnfbDDi7=Z{!`T_vCq~_Jf*}h zs^#huc;)L{y(_LpA|Gp>Hn`b7-1_iuhWbrGcabA`wgP<2QQCs?QhB<0uUeg&nvOJd zg)q;ZS3hU#G@BjPpM)eN_*YF8T`p$3`caJ7o7W1BL4WktrX*{hmw7YwgU)je{u`XF z1yv$q%IiF1l(yuz-&ZQRcm3y|_YbSixhm{%zPmVsZOW0Q-CD?WsCoRRgXN8_$$!3;)SL6q;nJ*bKgYHsV44TdibUf*JN4JG&)>=O_}!QHy6@N2 zW^DU@&HiictB&i3q;ffmPONwwv*D!Z`EGC5?fW))TSZ_S*0 z&e$xST{ida3abl$eD)U4XMK=cyub2|1N(&+>b`uH@`n>b&n@ZB&${)t`{Az#UDeGM z1s$GyZ+%+w_FVJH>&e?cBwQ)o|9<&9#>A}!>XOgSrp~sSJN5j!s^f7FHZ?2UTRP>r z|BL+_Uu&g56SHxvIrw;q1f!XB@BM!3ePQ*N3^!kX_;u;h8Qhk8E^In~R%@!!ZJox~ zS-1VfW7U;r{oXZU(V4mpt_Ir#zsGT9#j3VAe2Z80?%uC6-(28~q}Vp?EqhK~c>dSj z%I$F{!-d3EwmeoBb3Z)3)Ur{-wN5^8-qrVk*~wnPmFF}hYPp(Z(hq5W_;q~7=1nKV z_OKk<*s-N({x*BxTZ`J)T&Dq)GApZ>%Iv)<}*Xar$@ac*7s7`qNsxPTF^iobx z=l%COpa{7xk=*_IbI|QeY!gfvTFdy3^Xci=m24?;oK(M|>V)*N>jhcvQSYxUdwDE8 zK0bE$q4>osCSHIqFb4(m%F40Pw3fB9BThsBDY>o)TL@n-r{zj4~k>;Sdt z);ts4d$n3${}b8vW0UaBPZv1dSQxZ-v7EWbrhc1s;j$-;R{u^JicR>m@=2w`Yq4nS zd)JeeN>+KETX$#Tn~(}w!F|V30fz<=yaU}%PHMM?UBY?s}gVQRy6YA(yZ>7?%L&A z>2}(&H|02!Os2;-?fi@jLVIs^{!x^$O>Q||F*)N%x!}L^{_WSGy@MAFg@%=k4Bz&&RiHsa?Ba!!N_13^Msy8nO#BWX+haYi^zBlQrke z$(sCmU7?M0-MO<$?!I7{wRlbatiA5`jt@kx$(=fJY*G5YDbbCBiBDZ}Q^hyVS!zFX zi;v*tw{Brv6+PO$-xEv1U$br$GqrBNe*RO?t5dAMgnCjFiZfpQm9Ct`Kk?)8_mPJK z(w`=*+&Jy`FYObNM{R;U#V5ray2x&odntUcL)DGbx270#UD&X)OLFbsuhS+~?t_#v z37?nW&gygfeJo+xL#s&}^Zzc_T_fRtc4q%tv3tLcU+X@y?J~Rc9Tj2uDBXQb7mvKU zFJ zRXdVTyPAQ_%dbs-ope0^od4T>ziKbVv0d|e{i;fL?^lq0e+*9Fk36yTol0T0Ta}6M z-aI}YkvLXRb79i^SIXx=98s=^pVMa>%SVB=h%&(ptdyc)7!T~~d@!d!zjmM0JERBhsk4Nv|o`77P(<*ik=U$Ry=e_OLP z{F%WXMT04)KR?KQ`ra=4VdqX}%ck4+mdGEIjPTj4)nNR5@n@fRf!gX3AD<`&e-8Ca zFg+_=JUhYgcr5#J|LoJ#mkPTMq31 za57}+pVwX*XDU7m$=tB448PPH<=npW*oX9mo7Ej2tTDRzr-60%Otb$h%GIJa9n}@n zbYtDM`#96qY~%dL5vEJc&waQhrhM!06GMhI4_=->lyLsjrohMj8|Gy^f3vBi=2@=Q z8`fllumw4Rf%}r=KMTg3JUL(e`hWi~R#PqBs{QM%>pd9Xy>zYQ^Gg{I6B~LwLw2Ny z%7|IZu73XdYrWblzrAZT)@U(VT$=X4BWjo5RY{4w%l+R(l%JK{n6WwFT3y2N3&ut> zH+!1KM98h2yVt+{>e~6u2d=1#KYr@#ZWiuc>3L^L)ZMv^dp^Bgt`?N;l)}2)xOc-& z+h4*fzE90jRLP!|Bij`qWV8ErY!l~_};KcS`| z9)5e>7yj$L4?k7U`fxX#yYSD>%$7@^#oF_hWeZJyC!D>V^H3kxdHyqIeN1opo>iS` zWLvS>QvBzd@4-i26|UU0nZrZ1ERDIk`yijn!wu~mp*t4LXXmn(wi8cT`(I&G8;ifm z+heuMCwus;U|QWcLqmA2uJhm8ES^yH77I&$9iEhPcib1v+<%2fe{V)h*AnKouvIFI zHJNYE8Be^qCAfO7^p?MmpWeECV+#YLf1N}p!=+1?_g*x3zQ9Av$m98fRV;q2+#KZ@ zUtK<&4WF8NRlUU_cD;j$@%L}1AFcS^I(Ofliw{?An0EKMaG51n>H9ehi~%2%-`uE= zxm}iiBmVouyqZe|$=h@!Jb(9vWo1{rn)kkc)g;xwj90bas7Etbx)yY3bX-wod~*HB zIlm<>UyCdkFFQ6#L!iuhd;MayrJ@Em7v}KrEVtGOI;FTrvOD#_4Drr`hQ}`&d+zxX zd}X6VOs4++rsV$ldnz2b4@xJSd+SxN^X?Li|Nr<)@fPcQJ4=6BJ&tvLQf&NQd25K) zGF7ph*4IhdVV1|HzUchxa(Bj5hWihK*>CauRhT7ZduFBi56?sHv#px^`cDKtW&JtV z^VErB_mlR_TP(tSZVu1(@A>TJkGlL{uX?yBgC z*G{%ligYtRQ_4g_VIUDZ28~3 z@HO^{>i(xJ`)cM0w)x8*dZ(0Pk*whLGXLNS@5O}=cFzemOkLFTD#x1H`PD%aWtD~3 zzP+AW`FPf+Ie%gu#XiXy&z`_>>C}dfU2iTmd!1Nu{NRT5f9E7^*~;@oL}si1;^R@L z9Og^RT_DAt{lFuD*=K|Fx9DfD*)P7l?lwh||7XRIx3XFPqvDJB53?QWdUcY4bIyTl zYdSV}^eu?vy=~ z&vgIU5p(96-}U=pYko6b^=a-~5asX5sv@}Z;!`(mjW?p}*6wATtGC*0qTIolwMV$G zMyT}P-58>ye*WTaGxtfdrn8G}wuFB2-cs^?W%PN@U2NfjY}c-NKKyN#qA|h8e72zL zht&CAhdQ~h%%9!yRl?|ek;#KDzWAemRGw#vO}TVCXLHcs<*#>~x-GcZL`Ca@*p2nw zX%hkrJ)5;zdkdadDzYuypR4<2#*;_SIeE(tZNJDIKS?%ydC!#z5xRft=9$Sy%f~O6 z9i%ejWP~vm}4|JyUfwpLs~qU2GeVhufBIg4_DU zL?$<+y8QC~Hs}7ga})k_OKo?x7S>*T_kHgIo0Z?ywg}G&%1c<2BT}zi#B8&n7Zci z+}t?}l^(XOsGZ|*PC9X`na^=qp75n1fr4vW|L!xdbatBMS$tuk|Mr#ZjhS5HXKZ>r z@zsg7rSIig!|JpCFE5MmwX=IA^DTnaapkId?QTQW*W0zFu6+4dd}-0rf89CzDyL1m^Yz7lMxWN@dyJf6uo67XN&w_&e+Ktjeb^D>iio`u&`;WkpW#3Txj(293UU zRln38^y+?ozPz_@;(kIfHnOn`m!)P%l-K= zH$5ym_N26#w_VwHG5_SX{#lw&78IUW(v0M=zE^*?;K!LA?}dbZ|9y7;QnSp`OqV*f zM?8PK%2l=VjYOAB3UD~_U{>^}e7hw~83OC2Qdn9(q)%yyPupWMPw3xX(|Si1ArY~s z$D}4W`Eq6OtP-%=cc*u&MY2i29}}IcFD|%!+@oopCb+Li&RXm5^^fcCC$Ma+Oq(EF zGx1k>RQ8TgbIFM7>lp65E84#I%z?*__ZaPJgnh5AStc_5vsmJ8j`-K7wllu_95#_Z zTe?E}^@a4qN4Tq*VM?==${uo(4Q&Gv$t@MyX^4#my zEdm~KCleTWB3ruuGt7AWV&=rOpHm!{H@;lJo?G>LT~(*%(N94+j=#!1wYZeZ|8`pJb&Z-@ zkz6`aP3_@>$B#vnKRa{CKbNzjwXM%&oND?ZfK-_SIpFiwh66Oz2o2(5&&! zI&IpL@6$h?Kl7_WX-&irEvA)E$^rvcPT1nz`#)(;VfUZ5rH>MLgugL#t@vymDV+F` zJ3n*V{#V?IGa_PwUp{)z!~Ax0^72n#t_9Y!ymRbQ|C@Bve|oN;;Fkzz8uTGd2HTg#9@O_tu9>?-y^e7yfZzpTxR|&6jU1^_-q5UJ<5!W!>9|nmZhw z8w6|mWccQ-ow&bdqug5N-@@Mw+D}~!o~L6aX~FnU^T6GfE)Tv%$IH%cOF!LNSM^3@ zr@e=H2*ZN!GgB(>lv>W4qx4_TgJ~Iyx_$rkrE2+Ix0K&Lbcq)iU%2V$ek;wnKR2vl zk-L9k@t0dRI|bgI)LjxL%U#zIt2%wc)8&Tzk67v^&sWb-$oh9`M-2CjOw$cwff>&X z1e{g5d9SC|ohw^zu<=USgXwElq}-ELNJ;x~TPtk+r(JPx-MlV4WJMp?T%axX_jKZ$ zeGINwUrWBODzKL*mMuFR@k-a8|04UG``SxFH+p39?~r$vU2y7pvRo_Itbnq8fBY9e zXZlxSZN#p)gL`$p$5C#Nn{KC0t<&gLw3C{$d!GRJ1C?BRo{K9l&0IZ8^3^h(GpoKY z%-YivxVDp*cfXNU*xiaa&mSv8_neSfKjE-?xaHFc=hm8TUf$;EVIQJ9Po;LR_&>>s z%;%j>+1YTsnWlH=()>4t38KC;BRKVMe^ZO=3E1KJsw#9FOKYjc(IuDuJl*e5YqBCu4K3I*5103zH9rmDU1f3`r0KnXABPBTV@?-GiCMnbqi12-5#{q{#Mf; zdxbl$|Ksx=res8&>Ao;YE8~sdiGJJt70q6^V&A8Fea>TeB@(mdUv5?6OZD8FVX-R? z@p!H|th)QF>m9bAKWaqH-JLfs`Z39Snqe|SV9du;wa=Nm`PjX=p6c<>Hf;Tu?JSyF zxa42$$II=LPqL>U`sTQC;t7`ktA(GA%(D?uu&CanSZ%sZm8`0~^sR&R z4c6^zRnks~=(f+A?0xKC9^c_@4v9G%9fB4pA9UAxm}>0;bCdJPbxLVFC zC-wQj5(nmh`O@E~UA}Sfshoz!Y{wbdvt1iMYMkS9xf}M{hgU_Zzjlh=tizEJ)9ovI ze^;KowX3Xt8Q1kw$(nq3MX$CC$(%i8$6waIeSLmtLZev5N)@&Jx9fQ0*Ki+P{q_6p zy%}p}9J&3*`pS+!ak2$P*&0IpId4jiJSbTA*|y9jeQ6zE@UaJHuK#$ZzcML|{qww$mPlgb3VH-oWq#IP#usIm-FPaMR4}4)4>O}9V$nHw^6tqk zyqtNO-B@Y;o_TTGoer_zw7+~?d84FzqSA|?U(z2BZg+Z6#mIO|Tl?FjhkGVS?Xpi~ z^P0uHHm_vI6F&CY4eQ%(Omz)EB(Q$f0;h8lGM@HcJl`J7>W;2Cv*JqU+{IrXH;2V* zmrH-&s8z*~mg2lU{W*JWVy;4ol}+^~@hNU*P68^nix@96ub1oCsl2O(_n5<5y~MRz zUk?9PxBVr<&{`hiCi}YNrF_P&^K;g}k2tCDK_@n~EbaRpuU50lZA|CPx+k5)*Y+qR+qolR_>{^r8Gsb*E(aaGb2!t)uo)=FK^ytDhu zJNL%mly9~TEBVGGSBUA zzCp_0ligKi0WpzplYZBm#@$}HT%yzJJ)d0M+}aH;KkqCCxc)kIBK?-~WgbDp{NIo*3(L$B8?68*~O zxR$H>_y%zcjb#p7wVF1&TGI5J{54d6`U@bT~k(3cKMZ}G=G_ZR&w&GYusiR ze!Mj2_gtc#xT9jZ+SZ2en_c#&9LT*GKJRxY*Vq2T;IFJC?sDu=hM66wkgaP zR>mARI4yV> zs6X{G4;$+Sm2IH`vL_AIrt+ny$9^9w+{X2Tfgwy!Sh8`0>V>P0OV|Yx z%A}5OSa@5XQ!Q>v+q?@hAM5^|dA#wZYjNS7)m?XuuiUd?U|{~Jm?ynRK;!qx=zYxB zL%d4M-~62PYsa0dZ`!Nuzg+6P8*P50zhkku^em<>5Alf?{>a>6d>*!}(t&|-N#Z>l zwhL#s$CmG0@v`^x6TeUvk?HTRDr{aPI6b)c-0RAzR$sI$|90Md!2dSCgn>cVza{W% z9+%R`$fZ$>*1XA*)4RoYGwi6$R*#%zL7|7LE8Z|LYFHjV^n0CA(Y^Yuzb-j3R!rX0 z@ndCwQ>gRNc)NN3<;|SUCi*EcFmS(N_IcTM^WMsJ{q;fjo`kX0UO!v5*eP!Ej-OKl zzqou_>@!c!Mrx>)cJG^1b8TifFmSBan7wU5$k`h$OcC3%^18+EXFa?4GNbym z@FY9;61h*&>7CuxGKo#>x3!+i{d(=<`d+@{i2d*Bzc`khSK96qqR@U`+{$rM)B~x- ziaVMeCVlnLv`F6CBT;BQ>pc6OnRo6!zt!N=v)0)2tA*&MqhjV?yf=9ARV>PJ**0}! zz>M?zzn*Dnua7=@PQv9myOzT9GSQ#Sb64)`oXxw$m3i$g_heQP#tG(cR|nfpm}mNb zPta1+7W>L_g{Hq1Wk-AU{4dMi*kQlyf5NQV?UOdGTlJ;Cl!ZMu&v2Xnu25-VUX$F- znNf3-GnPe3oIYiczUE5P+NRCC-iFitl^xh>^{%KO-D@f8a|^VB1a@q@ z+Q0X`e!0kRv#b{n)4p&ls*@P*5$k8Mbv7ICoRgKFP6VpbKC6h$y3zwD1axkoaO?CTu+#y{U*=$0C!G`Lne~EQ{Xx2$$WV)x$F5cxlNACD-1%}9a%|#rm7kWRQ(x$^5`rcTGX-TT@s@4lX zWi?mME4@FC^JT>a@^{`)C)bglI{Q`_3R zn`PBEL9uPG6e0wFvz@HC>)F>G_ptix&s#bHj#8&TPwSZVFvv9U_7#a2vo-sJ-=EzQ zV*0~G?)TAa{m!>yLmxNnj8$w?aah_QFl9qawA4m{TVY%Mp7dKg{^HuleE;K=kmb1v z{mm!0#j;QP@T797rs&U`zFWkj(>?|&GW~z{D&&d(O6NcO*f$>vUvBV6Z_&HGyOxUV z-#Dc@wWVyY`x?E(=(Snu$oS{d zs?cp2bH3HZl*o(n_?FhLJ~T6t^Tg%ng+c|(CuHyLYwu@{Sh9GDpme%IR7t_=b6r~) zqD>->E&eF^;rE_nGD~lKSi}5=`>0V|Zw3QX>zoBCJB4=NEnaHRE4h4Y_J@doy{e~| zCOb)1EZw!etu`yZH7Dzg_yxPD$qb$r42;Y7y~5G4shK1D$l(R=Wp&E0Oc2>Q zPy9lI@v(EKu3S!-)xK!Tx%BN%PAW19ublBYB<5EAmN#Jv=DyE(74?OGIKG$rvsYpH z0pY!x7k}+^`|#(J>UM=9jmv54nd{&0mgBh5V_#kNrfj>hzL1jbSyrLa>YwL6r|-8) zQ+`?S`12PA|E&sK&w1v*)NR+&*sJs*sOR2kFM)+7)45s<8|OdxebjP~`DL+(dEOqE z)v^k>kNz=FTT;93)Mxvu3nw-$e67P)Ss7_#-t%gFW8Jp23?$M7~=U=Rd!`Q{~3NPkli*PS`PA zi!{j*Dfm;bZ&fPGRGkeU?kt*|{jy?+xDF z#To6vDsIVq`E>k-6fSm4lOv1Lr=MI?*S6v8w~t2y?Ye(P-&dZ%Vq?ehCn*0*TF2-2 z`o1$yJc+KEQCb~zh4tkM@2XuVzq;MAkY9HC6YEmFHzC=I2es!fm>8NaRJZ1OUc=w} zTfL6nKh@%Py4r53=<5V4KH+cY7ep*{nEaQ|!=rYZ_Wkq~_iX2LB?NxfGhX^*{$e|w zGh%AJE6jRlGCq2ER5mVt=_}W1tM79}c+I-Gr(|{bF{h0l-*#K4b6u%;-*8I(N#LDt zZ`wO{Sif6!dS*u(%PAHgC@BcHc#R8S^9jo^9&Jf(Y1Ta-mcQw*Z4~&{=tP^ z@;|;AJl@o`{Nc%u`t}Ad%HM5JpBi!YR9)F8&(jhX3MY-2?`{2exo*P0t=^eg%96Pc zemS)3fBBGqtMcTB2TU8Xo?cp5W^_dOkND@?`@RV*T$`C!Zm<4N;N_aLj%rqi4jtfl zQTl{uKUe$pZtqF|&*<%Vp1w`snGw4^_W{;t)pqNzOk=ye+EPyR_}v?w`fM8#SmM5N zFf2-8(&w=G!o6}O>vr9JQ{HS2IUv&eWX+d#>!0?lFMjrL>u&R8qnZV0I$u@V=NPE% zRq*?>myfoN~gmm%Qd%GmZ-jRov}UpkX=D} z{Jnj5d()bf67B>XcVlO?cSx6ftBK&VOzu5l>2Y)niL-)*NI2+ExPyJSY*Q4&8l`nGH+Rv9JAQvI{IFOZf?tpzpQBG5@)#M?)O6WqOan0 zo!^t*S+4P54p>>oTz@N9z3z#p^ZaaP4&FyIe?RcHT_8~t)pEz|r_Y~x4^*SWOzs5# z{k9}D`3ADP5Xr_9r*2o?IBU zzP2QJ;-STl4fdQiC`+EY=cVMy{5}8NDm0H(hx^e4{R{QvCE*iAF!1idt_7V6F6 zO0$~fzsYfJY>HW%U^h4UF)_Rs&>_4i;INwf(^Y}dxo|f4HJJh9>mKL05y<>Vd z>A}aTU0x2HKAVg#yv=wt|5CP%QP0M09IZVa&YoYAEY==Ui97b&;LE!Ev-hYeYF}v< zT(>{Ca-O;A5kC=j)Ei6)HVv*7+1PCvR*LpW?h^Yq*I% z>-(toN!K5!O{fZ)c{csl!63s~=f9o$JcG65Se(v%*1nlxwqJr}^b(6ZJ}~ZLI$!Ly z`H`_j>6_3^>CXLgEdQsSImq6YW4FIKL(WWU;R)W$IoEHdyB|4y>qEHvd&YO!Z7-7^ zyIv~v_+~7sHa+#PUdpq`2I=mqFWdj=A1Il3xZvRA9n3y=Kg>P1Rwi~=1?QB@S9CvR z*M%|su#&MC&yr;_owj|(KZgygsy|PEX=!81l+nuNQSs=8<$}9mGbTQ-I>Thg`bC0u z>AS;CA%2&xCf?QC=DjZ5dzRyS`4?aPP9E0Xdv@=ZBfIikZtI=WS!45d`=RFWaM5yu zxYNtpr-dHUT6`ZPJah-A^QrJ>K~F=)qmKZ|{dJ zUVHX7$9cZJy$_y??9807S4<@Fk?vSfXe6@o1y|G_CH}o#EVF+9{Glo2%KGy_%ns&h2i9(!@U`f6=CjY|7aY5m zw|KFZShAYJt$Ux&Gu&*5n|)t)`{ZQxiD~SHPQ25yEx0Y&o@?F_`oOii==wEziyZry z|JMA4lOE?JseZX+E-&c#=ih_7-(pS)?0voE*L?9$x1{(4Uh2<#;bZ%J?W?&`j%PiV zv3=U)@h_5RqkBm~`&NebNy)MEKZ#aeIKb?kzt(ZXyS;lkTe8hwexAzam^43pZXNIZ zFiRd5iwz4uKDg6Tw`9{go==4wtrC}eu1B`?{n6DyZf&DF6=p$=U9-= z)oC7)^3El{zCF)S>bbAT?5~e`c^7(5FHo7F_E+#~-|uaFj~rEJhrc~L^(z}^W=l-c z>Cl7+zx71(t&3M^6kU$=l)G_ybLuJk)9w>)TKYNMpQF{ZLpn&4@4%DV5AzGYdUzjB zetG&Bt5HV&)H+qp6AyN*3uU_?uaPb4lp^2Sa6@*(zhq|C*Q;Y?R;;uRXNlKn$lv&1 z`pOa=&gh%>oOZi!=uH+hJi_u#@>ty0%F9}vv*%Q?8&@Q3;GNI0b7jtjjICOKYI{_* zOD^{D!XN7VB$;9D^DlX3{bfx`eu^EH8H#)^ zDS2<7oXdSTcjJm(4fY1cZRNj;IC5WYkGyx`+=aFElijAg4^xsYNZfY)=rbB}s{m+$GB{a2V9gLD>L`(+@sXNCCeuF6%rUY~zH=}us&?Y-AOF!^D(rDl@dzB)Y$b5X)o#dCB=DYpO|M2M6VVwojPbVqqU)`#oQ22-6 zDQ79`%76{8{;(xKZ%^8^R3)$PYCvwP>WTX6&)jYHtoX)u!u~PG5sT9Oq1W2yDFz?h zm^$aU?bWw)RV55>mQJ4T;wE#Xb-h*NHO}*Yz6SGIE!lPWY}jrI7xi2d7LRgw^_dH^ zn?s(7Tfd0Z3z`>R5$AkR`p99@iOb(I}Xmgb!^uAZ+%yb zG9E>K))(hE#IVSq^XiOmhZOg`PTSQVzTvm#oRymypY&dt&Gyf9*PfLu4h@%ur$p^` zSg2UowzfL)P$#F;0mX|ttT}3j7w^6Qk;UggXWowa7iPI>)TsoAE&cBGZJJM8Mk*73 z?5uTuQx4lmaG0%3sB`#uKOK}qJkCoRqIS!J2s#p3_L`qSj+A~NeVFI?kU>Xv_I?b^+0b_-$}JI-~4 zZuIqDld$%?y!{{F?>m&Qv@Xmjwhuocx+ZOs-kQ@{Q}(;$J*$kkR&3>deaFRT+J>H= z`Ywn*Jl$RsP`uV#Pn!2*Z;R=U!*4p+zFP1#DJ*#ZLn!l1Le$*FrWodFO2S=Iv&@zHJ-x&dDJ%7U>sfTv9S*JE~K2=CgVyurCsq{ z-_%|?dCjT3bMtDQSMUF#*w58J&#q;a(n3Z_%{NUC<{ND@K43FF{f5t{UjZ)|GeRPE z`o&cK<+v?&;3k`1{ADG@KcX%L!Ow~mV-!0VEEG^-=4`#>-qK+Q(Hs%1uA1kz-rmad0Y^w(z{tr{%F* zPUpN}Q?zJY_u|=RIlsnZcPkfjonaSQrlfCVe>K*VW0(0g|G@XVCOUK-m{Tm%`}qBg z1dADWMHd|QsCUfmei?T5!TH*o{;Z9<4Zo80#e59IWC9)MA5>fS`&JakWZQ(m6^p7T zJ-YnMYV*Yv0rD~e+RS#o`rE}*g6_@zGh6G^Pc#3sf3=V4L~PVZWLp`TBlQ1c?rrwX zSH1T;O}esoRmC0`; z&v=aC|G`f>PY$2|5SMdvOX|bD)jQP-kDO*P*dt+m=c3Jy=(Bq(Gmppbjg?mEOn%?` z<+kniMxklyt6b#`p5B@F@7m8vz%-3k^S+9=cFJW2R^&LR%Rt85d_U4Jl>r>0Bd z`-FE5drK~BUO#&OMei)$DO(PwwOT7G?K-Oa`+%Mw@5TisES@))&Mx7#(iYcxd4BzR z4vrU}{?*%T_@1y)bjR|NDVF-f9snpg{7 z!J|>FwY$adE5D!iWy<4@y&3%7V%rX1p3>nsqr0uVv3l|Ul8a8$pWPQq?RvH8RJX6C zx{cayj$i3k%^}yh`D>GR-xlh;B^=N7U3}u=PrdI~Oq8->=F`9a{NC(eFSqZnIy3pE z)|&)lVYazn&rZu0^Iag#T-UaN?^0rU;*yL-CuI`+zP}eTf6Hj~-)>>0j_s92jxlG& z*6STQtj~GIBhT^hRwtLT6-s+{X59a8!TB#pxboPfck;)gRTU2V?&Dx!VEAz914DD# z;qK+PrH^I%EPwQ|=XC&!Yv>UMJukhr*W>I|PNlu!3z6RRT+{E;B2MSC2Kie3&XNuc zG3&)Rw>Gd|_Vy`xP;ozyOI|38X>oXb)Xt_>k9U6_mi~FknanXs{rREtxHLDbZvylD zYz`r=;J?%;)yvbPf6S!{FV&U|s8b=Jlcg+{XH-$o=Kxp8lQa`HmsjAI$u zIik!_TR%N`z`$U3r01sILBGYdy`R~%9$q#35H2e}b)ohO3061hKW`cs7_!$NV4Qa? zfPq1N`HI6V_ueotFfdrK)zmdGFfg#XesE!6U|?^Ol4=2oE0;$wFrKh{H(mNrb#;Tj zR86Y8^%b^5U$5QxuKMDX^V=nx4Ogf(oYc-q`*-PB`uv_Y1>WZ8oT&^9tZ}8r58|S~jG5yRoyOR=7Tm~lX5x|6wJ$(bV4?6RfPvvcHsf4Itr(_GCcC+Q3w$)xmTD+n(webBxHC2H z|EK8rA$8Kf?DzTI)c7xU&CKQBi5H9SegD5-|F29I?{ky+S4v;B+Z{fxe(b{TYof}> z^JjlbZ|47Lw|@VLUrqHrU#`FOKC}F7`P=qA@k{o9lv^NIWdG~$H2XtU^DFQF+WUXq z=L26E{{Mfw^3wmS|KHu?txx)I`cM8l_xb(u_jZ5#e*L?AZF&9K7yY01@43J4zxvzd zKmLEe$Mz@X-}fuxpZlNBe{bt)_j_Y}?Ek4RSYQ1=_y1A-^p7e3R{yDA@qfktvj5Zn zF+X7bCH~$1*S>A_$3L|E?*F5oVE=UgE&CVsP5(WA&;D}v@BaV(-{NQ3-QKtM@75pO z|LXrfzMTL6|E}}*^8f#PZNDY{{|S|=mr|Y|Ug|H#a%ba~nuVtdaxdR$NZqqJ|Cx!6 z>-#0jp&{C`pR&!Ezir55^Jn<5?C9gRWy@DFO_@3S1lqytbgFg=F4l?4?3w^@aTF!xS_fG)Rp@WR5q_`pVaqkS9=J*@^ek|#hg<1 zlfz4xoi@C_pf9?`X~VrcE1w;14}5B2YkhWAm>ILe#^WbmOjxq(K=k*pna6xs7=Rb$)zxh6}f9Zrj_zRi*qfU+w3? zi?X^_+8559|MHvhruGl_V7aMZ*BNKsJ!p7H=FZ;_{A@pkZ}6Suz0$h!@Y3)V4~?9) z`;;gA460{5viQ+w`NBJDZJiUO-%hjAu3V+Z&=JP}d#cTi{QsG|e)Qj3SL2zM*fvo) zVk^TzB^~XLE81^P?=qK=`aNM|Y^KbIe*Nd-s{%TnG;CONOwa5A*IZS;myZ6@$5OAK ze{*NTfuhr1mL{UU*QOjQ>i^U7?aR4-#^s;Y|7UroEIr`z_A_TL>p}6DOUp0YCB>?R zykPoT?X-uz=F?33>&m633%e$9o1D>gof@4uMIg*=k9_6*DKeMNxbZAH+iQ|&%zr;m zaoyCl^ICZC1kL`?ygkf6!6z&9#Kp<&=T4ujmI>JO*j7|A?Q6uON#=^@(|lY$W-a>0 ze)_$Rd#3>FzZFs96SeIOlM-?l@El{1F$-D~l;tmSF)4L-XN_$`nnJmY;Gg5we>d~I zHMsIlH2nI{?NuS;6h|FGG`=)!^uoZduf}vMI#b_Wr=s-2_Q@=pJD{0U~G z=dAbF>6HA-w(Z`2|GV}lHXh-dyYFu5SKZF{Xtu-KL()H&tbJ}_n0Reg`s&GXZ0iz# zw6N^cyWW&l?=D~IP|W%Fh-W|_7qjfC`ugY{zJIQ5S@U}4k%=FU9DMq&{m|uUB4O@d zRupCI|IAc6cdO5x53HXZwV3CA5-XeazaVRA1K;9M{ZOBme}0Mczj-iWf3}YcDACTW z&RHEY$>|GAOmNnt_QSej|2l8GO`bfr;f9{g23E_en_ESva)>j#UwEg#|I67`$G28W zw%yrYXz*u0AET7E>a5FN$@eYKY?vu=$UW6*&S8e=zJ&9~6U0yXcD$d)WxO`(*=@ar zHru#Yy_VeNwM6%T$j{Rytitu2+;$!JO~h9|>z{W%y~0L$9R;Z(LvD?6}qYzGTAI$;A_#Gt*fO9>xWG?5eL@^^_-NQkvD|*U6dRjXmp_ z14H+GReD^{aY=ptHZx6qo4SRO|a*o<6!IWozTUiO2o*6lG5~9CgltF7EMdwy5qC< zxT5T{dzw+xs2>u9y~Xqex~(4WXJPwO?oc&{0(Vc=wM@>gst#a+J13m=F~GhQ#z@OPcEOWJi^L6sRhXS_PNOD@Jx zyuYaP`Yg%J>Scl#nw&hty{~Q8e!uu%@Z+7vEwk6k^qm2cV|ofACuGwnY_vwNQm zlApEUoZX{%_r0Ii7&FXzxsPR^h3@IaS6*(L6&@SrS+y?l$it}h%AG$h-}u@+;Z?cF z)fKxJw!h!5Iy3J6S>ef78D}c=_cCd$e8Zly{(LL5x0+n}(r~FC%wF+Z&%G>Jm@nHZ z=BTRBQ@U2(Y^npUm2wQ@P8pPHS+uyfjf^b`X1i4M^{6EWgGYEb9c^#+i`~PdDwnUcb3_v zwSV@0o_4CyW9F(E`=e#G9@lfc`qEW;r}|OxhED~KUd}FESD6>Ad|o%9+HA_tdZ|WX zrM5R3MkcyG;*ljDdmq0&cH*zyt>eCGD^nY0n512mx%eghW16pb!s#G`YkLma|G$2g zhoxboE!RDMoIk#I z$N#CzFKMduIR&ce~NGSKT4i+lUM)H`E9J{w91nX{S+38;o1wbL+!%pLKUDByZuAtZTEZkUF0m9;`oIPf0-j z$7a=*kDE`0ua-1^wD$hoKS}M!mzwaly^j`1H{snVv+_aLOXe1K+l6;G{oYu@&cn=o z>_{`0sq)?B?3J3wd^}3eGxIodNgY;@J5m4YY5yz1wL<%+>%ZE0LsBy5-1aC|-J=md z#dPlky^GlNF!qq_UZux%GxsFy-G5KJdhIEBQ@@ao9QX2^Pkqu|-j+WmOWm#GUpu?~ z!s|aa#+y?-bgaa`&Rs91N&d4a>)a{|wHJx;8df8_G~+xmP*PcvEa?$UEwXQWd# zOH2A`{>v9#Th^5x{GcoH!;WLF;ycM@pXY9MyUWy>`TO*}HyIY&($6ng;Qs_43Uoa;*4%_2!1nb<+#hp2?q+V8-P5?l5z-L50eTMRUW? ziLG}FWU4KC_OQ51*rdKd|Jk9lzeCS=nr^QB8+Vdd(rerGyO(WpJ~S}8&wKrnC2R)I zpScRRuS!^{Fe~te2i?$HZ~kmw`vb2D(INSvDqrsG7h2ued{igY_1evYuN)@1yyu&7 zKJ@dIywxjn#KUKQHk42Xzck~RpTOq{f&l#6Zt_OzvoF8svm3in6vMObExXfcNV4#|qW zx>L5s-eu30oU1oCN}6k4Y7%+8)=#n4?%Z^jo>|L($bY#1-|hRUQUUMtjVf%bL-Y?x zZTz8bb?e2qO;awe=J>jF*Q=%Lf6R0BO5=URSeo`!+19jXxyMOAqXbYEU-dLirR3Lr z@2Zb;BD}xw%LJ+bWO6&q@6`TV`Ej;m_qAH9LAH{77Kr(JelX~bvhee1HAPpQb!62O?U&Eobvf>nmh;#0s#iwgt!ahDi=TX0_3JmM-3~$C z3d;(Yl|gg#WWPunx4oF>^}fOXoMK3F`9_y1LMq+s^2N41zr1+EZ_iKDKB}{pt$)VH zvHnlL<2-kl;JY`Jxvg)m2>U2!A#}>&h+1cURMZ1CFV73xXI|<=U1pu+GBbpetM$XuwH3vW)VV&2hVji*+UxnudIf{c^R}ooEW>pZ^i<rJbzR{>mj#R8 zC)EV(;`Xap@nXl^?kkH85*GX1mE_=5&-dT!4 zZUfV<@Fh>a-Wwa z&_z4DHzyzQF_mrpc`CiV->>h?Rvs(Q$-ll#ye9Df^yMo%`~){mFm6_k<785O-|(b= z^&)MvTbUB|k?h8MqSvMX8iUa*e$ve6QSPb-go znE&I8L-Rql&>D`o>8$TJ^X{E{$}25vQ}Ch_hd%o#h;LTdJCpUz3H>z3Y9sEO6T5RY z-XDmlXA`enpvR@4C&a+OIQuoK|}OqUuM{4&l2?hIV}!0PYTUym}J@9Hm{ zAUyBlgTI}%7F!p7;^LWD!!Tjtp8F~ro%MV?uDP%IF#ikl^Rs2&f4`kE#p(N#b^4*- zXV;(oJSpkE>Ph8^I~hebIjng9fN?^gh=V1I{t5;K&eAOxnl>>V*`uq)G1>NQ@-%@g z;nvN%3p&CoZ|&Cb*eekpf8zSuN{%0MD@?yV`gyZ%QdL_}*{|H69EV-Moaj(dZzw$I zx!&x>6V10t+=5eUOj&QR6fD$b=3ujbS0Z)NKyfzvDz0qJd2+m;*GixFUZnch_VdJ4PHJ<1CNA$?xKK=7*Q>>)fcs zy(aIV^0n1x5-*)>k=M&mW@lUZS7*-oUH)MYC+e`*I_znfegE5$Kj**BF}-x`h`NzL z*~!T|-_0A7Q=GO{pZr$#+*0^$cdO88-&p^&&0kM8AHJn!YPDllTE#I&F4o&`oBOW1 z2j+9jykehK?)ix~EbU}|+*gO--|7#2XH7~{47hd2Ht=dpgP&&j?gJ^0r4?jvRvox_ zp<-_9p-JbbU+`O`lC<&Xp1TR`o2Gdz6LxQVoh_qjU(a#YH@VHbqb?!7B=(O*QFuty zsq5@H%VHSsujpcTyXIxtmy~{Mf=N(Hjq~RxTihz%CFz{Jq?g|C)?#ho^qcSh*%%x9 zO$+8BYORaVR`vRc*T*}%Z?oYlmz^#6n|v+(zo4lyG39~eW@ zo;(lMc1>YmP|Rj}x+Bo|Z_agt+Kg6LhXT=e?^|vjue3M&c(e83{f*pHp0q3q`gOHl zQ&sl2CsSR@*9@cb60wJKR-Ne0nY}UH?U2a@i=B0bp&geMo%v7ZnBJALTPbuio<(KD zjnB_Y!oL>&etm0iZO=N3uP?W3{`Fjl{ndY-C8v(>oxUvYCFhCU1hK38e8g{s@AmPv zZdQ|=e$#>Dq^ADLYAL(&CEItsvRLRHi>iu~UnsD;tGS^}Y zaruK!#UeKTy0t09`B>oV<7+-2>D;nN^vJy|j_i=u&&yu&3FU24V_&?*)L5=tnxU@a z;C>;CvkgW!+W9Y;IrMMSPZf)aS{CgS@R_qLMD|(F@kPfD$m=bZnikY~QTvk8u>6T?ufCKNS<}2Rw)#Nb#Ophrh}MN)-fDei9=o#W={c-x7My#0NdC2I1!E%j zo)b$F-|UU}&A49a{tw-?-NBPj)nDni7QFv1FS_^D0rsnt)~%TkSHU4M`el|VDo+mb>BKKy>QeE>oM)keOd9u^W17?MG1R-+ zny49m7UDemBPT;Cyzk4aHQ#<}6wEU^EV=Ya-c74}4aJbqHcCmmdp z9KSzORc?Z#hCj>P6M?nTfA$A)v-0wMbFyanvE!gIgX5A$lbp{IEkbYaPhat1=byUs z+nqw@)GHrhHT=K&uH}LDF z%QJ7><7tbj`SgeBO;41+RRe?2T;(lamhZopK9Ac~UCz;v(S7;sT4(vGuE%cr1hAg^ z;kj0)zE*bkRypy-lO!*^dsF=Xz^zN$YNt-A&v|wI@jphF#`YVrk?;3ZyDwxam~i8# z*jf(3IfrIP#5b8wl65|Ly3+c-amLQICmnB!-Ql>I^2l)BstF05)^m$=(zbrSV)#Dz z!)M#0HVtdmI=tfu+q8a`FjM;L>#1{(dcC>Y`rRy!qccWHXvdAv75lG*$433S@k}f0 z(E2O+VSJ1|s(imWr>}FiU3ED>IZETbV$9CQf-`#8BSeJmFr9baWxsPx*oP^R+p;_Z zxpM78rs?^mse&t#a(kNBXVfbv z*WVF6pC%%xAF)39n%d1w%Vy(0N+}jRtTCSElY@5u{2y-Hv{R6^ef0tf=jms+&Hu&3 zAos?v+NB`Zud#)7-l+(NyAmQrx?#WiKm2Std|FWIb-{z(s~0=fc5>XmRkYt}xmBn8 zJGFOwuD|^z+8ORVq#r1_`|6F`0@)AWCxv`p<)qoLUBfbyzxupORs4x>wyclth#zJV ze0Ox~zf<35t=i{1 z%~bH&_V$8?#9uXxX)+FVJcc(;zB#+rdxmh`Ooa<)`{SOqFF3?dfBfg~@J|?Ow;uy-PTDBtM+C zMSt1k=KCd!3?S|@w zvz+utXRh5m-5$*omu+-E;MW(A2_>&p?oOGVcE4L$Dx!7Kgg;lN=b1mr>x$yG)3_Es z*ZjWX34v40Imi18t@C$uRg31>eX{P{6mX&MCi{OGW6myJn6>T~##K5v)QE=MPjdpF5Y;R0|)vka1z2?b#TMz5k=NMYC zdTLsnf5yS}^ZE4IuLbg5VkT>~rtwXA^*JE(s?mf40aN-P?0YZy`_Q+yORg60NP56@ zb4OLIre~imUo+Z`RR85^#d-7Y=L}uCaQ>8UNbIO;iq;+rqZm5xWY`Ni^%@*RfV;^eCt#Da<0vs~sR*>(w>yp|g^OUtrq-^$?NO`?{Z1pq5Yq>*`SoYEtUuD<4{XSRv!08{$kGXr= zM9jc!^xkjMWd-oH30d1LWD zws7Zd%I*uSdn~*JZWc64cXc(E1=n6HYBu?j5VQT^qT5bF&Q7nmE!O2wberDKjWM7ulK5Lwz~Q(mNk#1enrXd#pO4> zRaB#;#NEwK-Bz}}cGiIXPxaOrI`d>N3Efy?xq_Bsk#WaGA!bcvzM1H;&Z~Q(U-hohR6e#lI&E&N;z7p#s-_25r?R@}@i|wT zU6VckXGVkI`x|yb;+wzxyE|oZdR2bqxr0Y4Clsk%^()b5^)eNCHP2%!&l}EL+YeXV zJ)3fr>4QVrEu9mO3%`bV{D|XgS6uy1+okXFYgu832gerV9uc>@vn|N;I_sgzw-ffW z{=fT3cIWyu$7(jgPl~JmxL54jz`0n^yD^32b{m1;%Tk7|wC+>>tjyuv=v#QL0*3=swCok_l z=i$0<p(tL7*%6@DsvTRr!W z^Uou!xpyLQ`b+;O|9n2*DPzx;B` zng1PG5BpT5yJj6s4w%Yw>v-lQ%?D|b6Z`YK&-A?wcUYzH@Xaf2zV{2K7{*;Mc`nqr zx#;zmO+RK^<<5($(cUjvea0$i3%l#=DRT=YBTU~G&bo4OxA<{&{_-Wi{$x6Ad6D87 ztP|U)qv(1hQ)TZ$rk@|Clt*=Z`S;K(#y?fr!ok&i(m$EYVcp+VuTHvy_HMyZPR z?JyI^GmSx4=B^Ge-uzOeU_nV?@yDNw&KU_fD{#E6T%fpO(roij3uil=FzCMWii*{yw-(-G!aoOgQdB=K|rx-ptTU%Wf>A-M#~oY z{tsEV{^+(xpKeceng03h{_Tf;Ob}Jg3|$x6$<*F(TXQH z+JXt0qTc_mpR#zpDlb`=w@rVIZ-^&PvXmIR+UhgQ_+p+XUDl3{lQwkDDi=;+Soh9l zTC8v)o6X^~jKTj?-S#_FY^pri#Wmx3oW9(_IR`wJhnw|$aeOg-=a&MNDU;cf6TU4z z80jE;`_&5$$2sp;bzE|urFr1HdN#>@V^pot%KXQ=84SUv%sh_Q&mr(46y3l0lDsN3*cz?V}SG-sD^O&1`viOUCJNc`0|T&B_M3 z{Jme5UEkVG)~gO#lpUMj9#LMiC;VA0_gAJRmz3t3tu$dvjS$zaQMJ zSbphv*47nw_P+I7bLyGa@ivR#UHRvX|J=Sgzw!B-d7e@K*3I2A|NR@L?5b@Z71x_p z+y5R+&`wQ`^q<^v>JUSw_SUXx({{YtyY}hh-_pmg1zguV?412MW>Q1g+T_dktTpc+ z59N-2eQV9{)*r_T7xXNj6rRvx6Rp`H9^wdMH%@t?^Lgah8MU-VO7L&Raf(Zy3P22Pr0Jy{FXw@E(};`%gQv(e2dyYIta zr3l02fAsTh9;xt3sI6Hzy|l%5y46*oQsdl;{n}=~pCq%$^8708c*`~~JIeSB;~K`4 z?XUB9_1^7Txy2-EVW?_X;}wQ=ADynnY&i7hdV5ZW^b{T)?IU-qpL~7FTf=(1J5|~A zhv~||rPJKjZ7siKbm~;hsT&Q9Ot+=Cc%P~4ZBlvqs4QjmyZ>&JlNb~Ce|Xnd-p zDAS_f$JNF^W2vjO2&d!0c{_WhH8XbShV?Iwke`}&JYJkJNx($r?H8L3zd7c%DJCsh zt<1D=eAXHNXX~4<94^_y_Kjub6n?cv-jl-D%6>Ncd+rLa5RL1; z7aPz0H~;Wg`S#e;_Yd?qI4;!`O6cXTkDlz+yOwcFx4M#Y+r+7hOE=Ch)p-;nH|f#Q z)$L9{cBY)JeZnVH)OPfqYyGV)%}Nr}r}$~dPFpE4&GC<#ka=&5`_4;x%Fip+_Y``C zFtD|m+2t_HPrhjq?4#o)$aj@VBfatL0?9d|d)3ZK-hQ@c(-vP-!_@6w)2H`@XQqhF zW)$;^5=|^Hxez(w#MJBCPfsYx+bH_Y(|V1wLZ>rdpxU=9?>6P;-AoJKl4ShT-DUf} z_h$QkF48=4;Ah!P?UI?f6GA3PDBb;VX;#?Im248pJiXk5UsUyot8p4f zR`Ue2x3?8JMT+pvt`>X;moObt?!E55ineI`QF#6XSH)XPE7AQ>GeI_Cog6np%x~U-wpe zKf83H;@6AX#?7D0?#uZ>tjca?_Rnyw!}oA@?qM)HujYaG&;)`&i3d z-P3wE`n5VYoPBW6DniV*_}jl{QpaBf8>vrNc4_j`wwkb0>fu|LHqWiNx#;YZDLZ{; z*K?d>=GI<3agNMG{gCG(@+wCYcK_kser{7%a#e|SBSTe!pF(yKY^c@Hl=-1TY4kp^9#@AHd# zY@S_yHoIv_*0$fIuOwJ&X?WcXol+Jad|PR9!A_s*)(Ok>7_Y8W zJisC({`j+i#+O;5kIrk&dzNXsDtEI<-x9-6zS24Qo~m1HChE)W^OScDm%C#lkt*@H z>$d;tya%TeZmZN9N0vkp$<++VJFwyUYfdQRYF*%^74#k6O~dMwXn6M6GCW3J7MORkl# z!cw=keA_WMkp0f|?K4=P@4v_JUryid`s{solvZ7{I`8~&Rl3<0_v=5JpS)K7Gv`iI z9;4oLYq{v~ROyFXqo5*|CNNAHpr|O>}SmIxO#B zB%`!a_^$U}@AGF)AF(@k*{ATT=-uRJ_f$N#-97x1=c9y!;5Xri;b*6|A71usf$_5J zxoY44I2-|*l3Vcz~N+ChREyPTe?6kJQ}y4YCpg2D0EJa*67aksy)>v{c|%Nj5% zhHLdEflIG*idTm*)K>Hcew}@EM`58yIyd7LAM?k(P8x3?hg)nt)bc)iXN&chw>cl% z-X)~m?AyQ6$}cqel<24XZcFFgoH)^zyE5$V!=26U?sN1k66lKR@&*P51_nFU&HLAuYi!70xpVodh_9A9@*M?ROtMZN zGizJDOEi$h>2syeJHsVQ&VDJ^{k!AIPunir0Fl5qx2h&wDYwkfdXse4uT7NYjqs+f zPy02$6x2>!wZ?A!su}0zc-;Hoxh?H>ReLz&&#c3rCzZbEPiQ=R@&MC9$?)Y~XEWB# zdl3A8;k`ebr!r5!?(|l|w9!WHj}G%OZEZfg`5V{Fn_8ZAN2{5S6SY^+k~ZtV9s(q#4r8hF~i$U z=eYx_#jGE0>#2~KsWImiYntteLyl#K@=WG)-U?DVvs|dnt0OUW#ke9r;C6lLJJIUnn|Nr@1!_Es! z@&%vH-qRBGqVDp#E05Y@rs*&FrBhP2<>{*eRzuC3(Kk+58!mCV;cLvZx+tzonr(e} zgw`p!zsIyEJ55V>Sr{q(LH&v8@+XGNU$6evzG{=A?K|G583sq0W!JRxY?OR=WrIiO zJ8_V5zoxdaSWhQQ-J0RC-np*SlRl>m7@KZAiDWjjaXB{Ym_ny@f@X%g zq2()WJt3`~A{Y0aPTKUNpU>;b~`ah}X zozUOi{%h{Ip5D=O%v0NI^P#dgAq{(`+$j-fG-_n56Eb2*&uXku*tBlel<;fLhtJ;B zo)X%Twe@wW!M{7#nj|elwUTw%8x0w?5rp zJ?GU@<=~uG`V(3TUR+7mpSZ=cn8z?BaRTT1p7Tq$oHPG;eA*q}yru8=d=*mFC|BMl zb~e7o-L$|^?R;=Zro<6RHjh*9dLQQ~mG>wm&p37Wf~Vl9su{oUrln!|sV3 zjgzxA)joIEe^Mw2xgC5ZT5#@XreD9W&s9pT=e1a2n<@Bydi1d^mb0vc`?Vex87XG2 zl-fP#34o{r=@haNXH-cy4F6)L$?;Fw@iEZrPaFLBL43Di}TBG zZpz{dkO_%&~pr z&p81G306~={-}GgXxA*^Io|iQ1U56NU)IhHyL{+vN8hK*8JE{hKM*G_oM*o3`a9uT z#&6c$!F|>?LQCSluVASy>v{Iw>7`WPyd`Y9QicvotF~W^%Z?1Ne0=)ijfh&FpE(f* zf`Z>wkMum0aq$s2Q1N|^#)|o0^p$2ze|vZ)cUkCJ?pSG0=KErC`(->%%e|ZXHDmEX z*Zq38Ha_~1eRG+{#He#mlwWj8L@m{M(z$#pkM&b~1-2J~_k}$_ORQjcJazpscJqm)!%(O=RWbo0~WcJTjjn9zq)H0KUZLSY5o&SrXA)p z_SamxTy)JvM!@UduS)j=${Vh(ijdRk;bEUL=iT{-33#U!I$#uj0zcI;gXi;ip1N|9(lg%5Tpt4$ceS!ocA0oZpFk zs?VY&`@eL(`90V3rRmC~SEjs~K1vQ%%4{jm4j6G?obq*^Yu~GX$JgF!epZqo#kT0& zsm!#47EH^7R_110{1B}WpM7-y-FFL2>ZZQiaUq#^QtDx){@RK?UtbzKFy1tMXHaq1 zUQP4SGNpLCbu)_$&*>RnpFf+&ps%7wKzL8e=a^O_^PdJ&)}A@b_-0?g>g}2rzljM= zn>nNWokQ!I*#If1Q*CKHZ9J*FGxFGRvuoP7PVA7g zeiW=;&3fE}N4%fIoH1el?EYdNzKpJAoQE1BA`048?wzP=tg-P^qJ-p+!>_XHIha}X z4#f&7Ontg?hh1FHBo~e@k3}n%HYGmke8=S!w7Y?!OtS1n!-MV5J^iFV`>7Od*xMJ{ zvYM?)rrOnQnnUiI_O-${+rB9%bWK@${>VlH*RxhR42+$bYW|xhpD~@9xY+fxpa5e; zo99BeV8v~l(j{XSZ>vhGGcT)Rlss4YtjJ*7!o_hL)0eJp{IFWENxilsXvLMr>jEfRWUXIXUGA$1jnD2}=4oDbW7n>bHT_QZF}x->3)RgxigTW_S|YY|a`KWb zkIPs7UHeAsf8WlEX;**$T0YP6m#DLgpZ=2HOE;Wbwclgo$(+6CW`1DtYJsD6I`ltlisQByeBxnDxrQr>~P=$+z&{UKlfTv#6=9 zpVQoJ3OBj(IVLbLI0msyHMq3#BB`Q^wSx;CMYuhS-LcUufzL5hq=wi*LN<}&J6wFvG_CZn{)v`X%z+rCdu2? zd2_CH|M!^HbmdX;?TzB{yRQ5{zx%|?C0x1cVph+(=H4{>vT{jXV^Wiqc;z0~Kjjvg z75iL$eoS3lAGhOa<)_G%e2o+}LV*6f=tJ!Mnut>3BZ(!V_a zZC78^6>f1~Sa0p3wnOZdFD<`j&3d^baB5chg98hSxLUYQWi2t#JXiCp?8~lgwR--G z=2i-7o;oQgcYIOJ3&*$4Tjrl*X83Z;bAfPtlET#Y8fF!@8%h$KxldY4TW!zQJ28Lm zVV4QhS)9xrSrbp0XsQcr%gxUVJEQ7m9XtI>^Y4zb(53n2+F4=^q2EHYx1LGX*DYUr z?VG@(i^~p0O)I-$UDq4oo5uBHiju;PHPgV_O)lZ=%;?KMrw@zi> zQ^l7j<@;%$hnK^02d4Tfa!>U3EEl}7U+j7Huh#)RmmaxXbo<+Ux>-_Aspe1D*>-nX zy-j-4cKa~xJnoji^8K}sJxguwb2l8^z__V`W!j(id2b&4UbObq|KDvF;-1G<&GQM~b{voad9E_kfj9$mcc zhxs2d?wjF91cf9VZb+^UkPIpn+{vD%Al0JJle3OVs%-)L-two*<~TmGy7#gC)Z9XT z*K>!YI|Ge$HveFM{^nH^+x+vBR|@lSKYfwo>EqQgdGf|3l`X4mk5x)-x#8I*n%tHe z&TGSO{ZwID#*U~raZl|g&0DVLed@Z!fvf)0k2rnV{l1^mrt!9AajSx7u&|HFHV*+q zW3j^@LK~-?zE*9ZDM_1;V4bHD*e=I#%Qn;gPtD=P2ohOTB+ZMn%Ma#kWEH&OJ6B{cfh*_nd)>W>%g5YLp)?@qeGutMdGeaoZ}h zmA#kE));;2l5}LQ)Ml#r-QTiy!2{oIhgG{QBzC3dOrEjp-@4BpJTZEuFW5qqcos>ESlNA> zb|yn}UC(8{1|3_)seU(%wI^ssGjwUJ_GR%n87}Uh)ZJFUfaz+|xlV~7#hzyc)~w&p zXK|mrdFs=g<2HwNcgRkR%IvML2WY3!q_e9I`4Q{7p zv`cijCJF2mPyOo|<2I*8bDKr{zF^*WD<4mPSD>G`o8wx;GS5?+Kg^zxuO*S^_UMb@ zGQnAMJEbfXl4dWyP-46I;g%*1Plh>v9J$ZsUCa!b+cJfn!^Ll+@v~2?c{BU!t)4tI zV!3kj${$wAIR)L{mmgUrvQFW#TR|hAi5!DiCVymKe2T%Ur^@$Uyos=oK6S3=SJ3M| zkFUPlg$h?(oy^aaE&9Kv;nQuFt`}vSxEtc<+zK^+@=i#JFRZD-Z+0$+m)oYm!y11* zmrS#Cl~rYCk=iWfe=|kL;hv4@e08~;e;eNgX_%}GW)pk0#<;^hF8|t+vS;1BdYb~4 z#67jrQNLhut7PjB!J{2#zIfKk&3^ez-j{z;_vLDXdq-IKu4yqmGneE|KM{R<(#oA# zxxN1IUt~``*!4cAV9T@_yW$r(vrNo=&3)>xZ(I=5!o_^O{f(~L zm#$dWpE(g|^|!}Zg7y2C5_P$=l z{xfU-yK8*WEZqGY*MH8d*`8?hQ{~d-NkXSu*0QX25wa=E+%fH2q|YtAr3EKfRVqsV z)n0#6rR4kKN83#b*SH(8UDv)f|7J$HS8-JAMUmj(%q7>qeCgRFJ3Bx%>1^YP_8y+U zi|V5}U0QEA=52mu&asCpYjVwlZL1{;;%6-@?`-BuWP5qgRVR^S66c)qUQ5T)-fLe1 zf0{n{!~5_j(~;cfct?kvKx<|(@12a7yH++YPH$s!`Zr6rVD&ck_j3&SRi82xCkDRQ zJMq;{|J7@b2>+5%as3`qlQsLM$%9FGyalhOzn^p8>Qa^D&c3@PYF=s+q~--YSY)f< zE+7Avxpe)E(76-tK5$qQGI!qO`u6>a*P5;?-99oR{+1JiCHw?%6o{EUV*D_hVuR`mTQOw{XMZ zQsrvNR`1J;uDsn`xxeV|j2kKrH~FrAnie20{@}-xD_37Bz3N_*aYta&$<k(irAvykM9$-zFy*`FvRCUBFL;O_KFasN zm9_ZZ&W8Sk6Fvo-B`o&(+B33oy?VSyV^i27kCcYH8Al&%3^NorJ`(x1{dc+H1PS(S zcS>gjYfYGTq;mgvVFe$-Gbc4S)zAO9tZGSCS@$B3;!Vz6JX%i}52o(;Q01kf@9Mwi zSczX*@tJ=Q(oACek0o3b(-J%M=)<Jg0YWr|P!fwMU26MfwU0FYWl_b@yfAZU_ zfX`>yqC5O@p(fUSYk76om`wQRE-%%#uJrN2C8rInM7>ID8J@5)Uw+4L`{zSJV9cAc znzF<)!3z`L`z8qF9W?a{e*Ne_hqc8q_0Q(%M=vkS*j_mQ@`l*pn^C+Ilb>*}fAV_q zraLEX1=dX7EOX`iodwU!9QZGP@!-yXxBtiPN9>>WDk$6cPnlnCe{+k^^$)jKIx*Q# zQ}j_y2wEgR;liidr7Zq|Gxh7&7s!fm2WZVb__|Lv^Ne!jMaSUJ9MxyK0`L7SU9bN+ z&5`}%q23*7se+f@b=w9VP+!$lE35Edig{xE?DEfn2i80~o^F~__jc~K_*pI0CpIu0 zZg`ZPY4}ROyP)rY#2wy(PMrm%vnxw~d*7b2?zUFO+12e2HwJ&7I^BMU^`ZC2HPpTb zXH1kabU2}{^H}+JZuZ>mGIAzQ>=*o(|CFb6N!a3!8sndrVz*+%4IS7Y?sji}zo74Z z;rqEckk3(#*&Gvp6olRv>}mQAnngd zDV{fakGT&P3g}rZd(XjjaLXm{e73Xq_9q9bJ80JI+$U#vzO(pT()$=*hi86ng?H5- zpEo=C*uPBkvi0kk&lZ39qVoA~#$owyKV)V^-}Wq?e0`SdM59+K{aosj1Ij>veg`TtWHiK|F#^cbCs^JwNl*r@veB zzJ1zkzuf5A-!)%;-BC>_<;!{bz-jWd44=AG(`2qdzb(EpHY+9>^M1)+u~5h`A*5o5 zMyTz6QKx-(HEKz$31RJr6#d=h+tDeXY&s+KuyP zyEbT)g;iW{K3W(|jN$Tu1DD>Q&ok7<%Nxp;kO$>r|#FeHElbtCY|v;OXUPV|Jn%oyVAOo zes-?Vb~9bpro2k(Q`4l2bA*3xIP=FXKkJXp^>@7v(?Y&YTJXJ$eNW@eF7C1o-tVuv z#8mms|9Z4z`6=DKT9tcdZ9a5|eRa&-ODUFii&^JPl=6IJXJ+f%F0jm>^{e#n{QQnd zyLVa(eRyB{%h!qBQCPu)m+R@BT_19Vbb6UX^4p*H>?l1{ut)gG=}wj;P_M&IbFy55 zo#@oX{@s&bUNJu;H?L=>@r&-=VN6jL){L`NoJw{+ztO{NnfHm=X2Z(2Kc`t9owfN2 zXH;^P`)9E&Evx$XM1FqloVet=(~)_b|IAtb`Df?F%Jp399PjDRooRL2XKq0ITyDwz z<+~RgvFEqh{$plp_FT5}g*AtH*4W=Wt@7^Jq04P~y3>TO>b!pIxBo+Ef&-(7etqp8 z)7*WQ+`peq)u^v9tIv!7({W+j>|<);LYEHY_-qiXw%+--H-FWVm#34twz1TC|IuFl zFz1ip1@`Wis}tX@;%c(GtG{$=bxl22(OV|x%{)GW`K-6@wI;lqx$NwrtuEKs$e!1Y zIdCxL_*DOyUsv$WR{#Ai(d-@PD(CreUVCkxz6&dz-0k|NOw2Os%iffei{!HIPima= zsyFWEL;0BW>z%t!34|_I`V+Eb3i|~yhXaxR9IQ72`Rw;Qo>{)_OuD~+oH{4VwJ!XB+t1!DZ?`rn?!WWkZh>#jdY6>e?PVUhCHd{Z z>h)8%JX73!E&tsv_Mp;B&T@M~^Rgeznt9`dQglX3#m1roCwmGccKtLrzM%7QX?pQT zlf3E6TJOhx*O$}S=T*%SIN#~1p6L0h78YF9z4==opLCm&ArPv>XB*J$Rt-$p`=k9D3gEKXAA)SozhS zFUclqyt8_{!e19lUzphU+s5?y#Q&{6G22dmK4Cd4`0P)c1AR9RuY9=Si|-wMliA^= zUnRO~QVvg#+7og+vR~_D{F@5vgn34+hqh}}hVOWOG{wPg_GzKVzRWWhUE}__U^vF=$wPN%X{N*owj^;58M}$dzUJ?) zvzAK7{N`m^`8aX?iMy4DlqRh5URUv~BU|ZIv+k4ljnUUq z1n*66_uJ=Ov`pog_`EZpwwpeX7e9Wk=i)x?|G(JHWzKsUmY=><7(RJTmOF#%UybWV ziXM!78?y`Mf4<(ucKL#+&5td)#Xix3S2?wKPIIYWo%1E=G_T)@a|vxO=6SxJj+t?0 z3QSwIz843{nq2Uya#t%}w%Ho$F26Z zC72b9UX=fT)0RCxDmRetP15xE`Ch~T9NV{g znM25=8`FF9qzxF<|DQ0bW&U%3fq}toPW)Z&(;Iimty)|3_E2o7H`~XkmyeFj)|=J; z!KF|ADxZh8kJ(bre~wxY9T%ONbWG)D>&G63Z}0S&UTO32>MG1T&cLv!N?-Ea_U+mF zk{#QZeXvS2);ju^VeOt99``oC4f0>%psLID&@X4*&cdpRUo(GQ{y;h1)XHRU zEEK8y_TI?tO0S0RTkA6>42BjdEIBfd?P^$UR5{BdzZJ*64SnbMwS-^6HU2<_^}Cw% z#X>Va7b-8f_&%*h=B>mj2LV2rnpOMtymq(ihaD~3*ss?(^VPov3X*_kfA$OJFUx2BH7oMs_fUhEw~gjxZs1^$U3@n0%R86iQ@^HmpZ8wNKkuifvfNao z*wD;U)t%eoBv-_HX$rmC;dC@>kMsN!6OMnm-5R&uq;GcPZ&B_)&nqX|dc86?3A-$K z%hFP~0# zN^%c;rL#P?G4P1ov{Q3Jbkgn_l-I0T^jz3#>ovC9AHrgMbxoel*m?OyVZ_{P3tH1( z&*OV}_2c~MrMBn77z53?SMVFfcIWFAbGAa!lqycbZJxnHBpJetvji(Y32F?bgls^dFecF zU!3U;W9zVQ`E=(e+#piwVv#JxOpzaH}g&# z`)#k<=E;7`L-L|{+^j`n7bB-+*IKJ^UD{sS%yDpux^7=NabAFxV(KMs4e9dje zz+%?8GhP=I9a001*tDOPzqm-hHlpkN?UaqXge{&V-IcyH?@i*)%lnr#h_5R7#c%n4 z-vgz1`acbqixVsvF*9&b*nGnwg<6d!%>y6544 z%Z>Opy!6ZX`h3T?Wr>eN(^}Rj*!u}|SuA(jcA-|QvG%KY#Nw+jO4eFl`a8SXaaz!^ znTncWd^>GEd}0uNQ_38_!}iMOZ&H=(YK3OD%O5+}qjTvMaQ!2|Sd+expj^ zrP!g@Tjv*jm@+5pZdFPBgnk8`tf|)yZ*}DR7o%(&QQYNv_QG{mvCwDdRhZ95d*0Jo zv{9})G4f93QTKCCtxSFIetQ(Yc$FsCmypsa8c&awcd2u;_?c!sT*wftWHpz|!L0mL zc;>Y2YqA&C?C=eG&E2Hv}OASetYAkv1|c1T4rXL9h^U#t#j7O^7>OjE&>OBEEfyqcpE6yXXE_*Dm!y- zsiawQ?7i#HBCpO@SvArAQ3eA81B>Xt-%8i>k1ahVS^I0T;&Ypquby0Udv~T~54W=u zi|eb7fV=N+)MsSey!*#9V8P$N+2`M1TeIM$zmrV0|MWd~f14;Karh|9sqVXK`BFOF z^KinEzBSBtFO%=+{avt`_v`{jW9Bu={EMy|hXx<35-rjUu6eLNb3aMZwJ_f-7%oO)YN-*DR$R^>d= zs1)~mc7ku5t~;K2bf|gtvgcbHqm6_zU1ro^6Q0Wvouy2e41M;op(m*+Ipe) z6PsmznWQZFe1Y3o?xEVh;|zxr&kCe>6uIc$`nlOE)kXPb($~Pu>pE$NUNs;7H*s4@ ztF@oW?O8Xv&IUL8-{Lm7+j+i!UXsWZQQZiq*6+W6i70=W`#$B2gU7x<9J6L!ykxMi z^qc>cTRw4Yx|YX3ykb>)8{*XV*3z$UUe?8jka^1QnusQNt}E}@`;|X0IO(4<+AS9rV0EPK-K(IACx2b0 zXk?j`WN!>vqGoh#nE>C8ie>L^hO(br{bgI;nMf|a{41*hL^4#TpX-kdc>DAq) zgsR+Cv)ENEgw0>hzPXZlx7Eh;GB22`Pxf8u)>T$9?zFFTKe0aPV7JZ$X3hA&F}u|d z%?o(=%Dw7pO7V>+o9sHpF8$+s*>T?ZctOT@H~)8uwtFS6@b?!nT@s&g*Wl!NzS;L( zK7Q|h=5V~^(Z-!ZjA!S0$_Dg($+Cz!_LT8F*BRakIl)e6y}RbcxAfJ%j5*lDV8ZP9 zX8XZc*FL7F#R;0e{gN~HWm@Xej+#xAHQzC`AJpI~H81$D`>5cu=fZ{AyVmUKynp-4 zh0D#i6xZgbH04M2sNFxr=sHimp{Q;D+J{@$9iKmSZT>&YXSdv1|1fUf@rt#0b-MXi z%brbgrAMreB$J4)i3Wlydy-ZZb4$3D)8_gst6hYU_-O>vLBbEel1 z#bn-|9M*TL@{8#8p5~%1?*qv`XZIaYD@&+;YuRo7d~&wvk?hS~w_}1O+O8f}OFPzm z@KTW0#U8CQ|1TS^oz5Nff2|qc+M~tpoLa|!om%$m^&-Cy*A!)RT$k5wUC`LldReRS gYM|rs-PYXu%62eyG`qjK!~B=&xRBGs;UxhuvT|^6v2%e8;N{^WNh`#b|3?@XF)%Q)GJ*jE6fiO| zv#_$Ub8vETGcquPr9cEk7D-Zofr*isnTds&m6e5sfq}7>k%^gsMUYiU(a@1iI53f2 zsZhkIapFP_Wv7h?MT0JWP%%y_YU1P)6PJ*bQdLve(9|+9H8Z!cv~qTFb#wRd^a>6M z4GWKmj7m;PO-s+n%qlJ^Ei136tZHs)ZENr7?3y%r%G7DoXUv?nXz`Mz%a*TLxoXqq zEnBy3-?4Mop~FXx9y@;G92y!rKbJlOVGo zgFVB>oNVeo$jmijMK>TG-dGgPks&k&zg|1ziVbzs-oYwCRG zl{RY~mp)OpFyP3W;0Ep$&#$cu6^Ykdd+VF$Ub(sAlMR==ko~*g?+~-=mq5(ew1e zCI$8Hn)}t)c1!eR+gI-9H$NOb)&3wQH~r@1z8N<24ix*(Ew<3UtFis2X7TO+3?B9E zMWSjC-{wupySDDu(z8Eptv6+)d0sC`S1{JAls7VTllf&d@#S%uP}h~($MauGp3>u= z=5_AmpCwbK-D44gIT`fRuD{~7H4 z?XTRtTply;)AU1^?taT&92VihiDQN9v!`9my%IO-!fWty!>PIf?U&OxLecvqII+a;}r0N>>zTIUPcj~43PfyV|TTbRLo)n_7lCj6yI+yuOZC6pn z0rh7pJ$#i5U+ml!KmGY>%cY+KrtW#Ot3A5XvS!Lc@gL{xb-gx6oy?nfQsLI-l@;e4 z?<~A!RG}_!W$>K6=ZnI{ZSy?;{^q+|6?^>Iq}b3b?WoVfrem+{rv5}~io zIQ4lf;CRð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 diff --git a/doc/qtdesignstudio/examples/doc/images/loginui1-align-entry-fields.webp b/doc/qtdesignstudio/examples/doc/images/loginui1-align-entry-fields.webp new file mode 100644 index 0000000000000000000000000000000000000000..a160b0353c593fd3d4ff17185178085ad4fe6c9e GIT binary patch literal 7038 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 literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/examples/doc/images/loginui1-align-push-buttons.webp b/doc/qtdesignstudio/examples/doc/images/loginui1-align-push-buttons.webp new file mode 100644 index 0000000000000000000000000000000000000000..79f2203bd63c467654dabcd4c81af3af919d607b GIT binary patch literal 9478 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 G0|Nls6`11y literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/examples/doc/images/loginui1-button-background-properties-rectangle.webp b/doc/qtdesignstudio/examples/doc/images/loginui1-button-background-properties-rectangle.webp new file mode 100644 index 0000000000000000000000000000000000000000..32975bccb50f9b267d6601884403372c8b3e1474 GIT binary patch literal 6750 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 K^)9%Xj{yKHGgq7d literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/examples/doc/images/loginui1-button-control-properties.webp b/doc/qtdesignstudio/examples/doc/images/loginui1-button-control-properties.webp new file mode 100644 index 0000000000000000000000000000000000000000..43b6a8f398d7676a178f050d04f796452b458341 GIT binary patch literal 7578 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 aKReVgmz=$P-%fA?gT&q`3+)vc7#IMYkdS`> literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/examples/doc/images/loginui1-button-styled.webp b/doc/qtdesignstudio/examples/doc/images/loginui1-button-styled.webp new file mode 100644 index 0000000000000000000000000000000000000000..5295043314bb4669e2f49315bf11afa1dde52ebd GIT binary patch literal 12716 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 literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/examples/doc/images/loginui1-button-text-properties.webp b/doc/qtdesignstudio/examples/doc/images/loginui1-button-text-properties.webp new file mode 100644 index 0000000000000000000000000000000000000000..4ae57477f2293a04d7f7fdb65d79cf8d39d0b23d GIT binary patch literal 10352 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 literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/examples/doc/images/loginui1-button2-background-properties-character.webp b/doc/qtdesignstudio/examples/doc/images/loginui1-button2-background-properties-character.webp new file mode 100644 index 0000000000000000000000000000000000000000..f41d79c4ef292dccb41568713c551480aff78579 GIT binary patch literal 10328 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 literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/examples/doc/images/loginui1-button2-background-properties-rectangle.webp b/doc/qtdesignstudio/examples/doc/images/loginui1-button2-background-properties-rectangle.webp new file mode 100644 index 0000000000000000000000000000000000000000..b6ad5d7f9fb6b8e9c40d2410f853aa1e7b2df667 GIT binary patch literal 7222 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 literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/examples/doc/images/loginui1-entry-field-properties.webp b/doc/qtdesignstudio/examples/doc/images/loginui1-entry-field-properties.webp new file mode 100644 index 0000000000000000000000000000000000000000..2064347372773ae4be986bbd866016db1fda1f1d GIT binary patch literal 8388 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 literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/examples/doc/images/loginui1-entry-field-styled.jpg b/doc/qtdesignstudio/examples/doc/images/loginui1-entry-field-styled.jpg deleted file mode 100644 index 87f2492005904cefb30d10ab0b42206f6ad5da99..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 45781 zcmex=aD{>EPXhykx&VxJOvz75 zRS3w)FUc>?$S+VZGS)Lx&-wC14!Nf+YBuXJRBSxoE$uyoIHZuT-<`9 z{5(ATq7uTwqQb%wf;?aVrNC?qf{~Y-o0pGQfR9f=gpZF;1Vr$OK(q)#C>|(cs0W!D7@1gD+1NQaxwsk8 zB^eo*m|>Cv42&#{%uFmytn6$Ytjs*dj0{YI%q)sRhOCZ(iNZ>SjYe!D6BllDQdSWS zI+$c^Qq*+OnO${K@P~&VlZ#Eo7Hw))bD12H;(BOtiJADN&_}7CHn$wUT>AL4oBEU` zty`uZx$@-8(yd3YKK&Y&R@OFc*|uZXo_#ZS4^J;|pT2zi@$1jO3oNTMRtRU^@jF>=}&zGaR$`DV=>()@{T4vSa(DGj3hm zdEdW!#@%`U8Q!j_f82dJd+U*_SI#e8{aXK+Zd={dfBNOyU+!ENV|IYU=w<#z>z=-Q zcWYOEJ6OqAVb*k8@x64Tbj)qVd;b~o->WQN_UroB#moODyu5nhl+nl5N~gE)MI-J? zrr9q!X7=mO-E*5?uB#|nwu9;ZR)L2*1ba9XdT+TowMIO9E$QqzTgI&EdG4KhE3pZ` z_Fq+SRN!U2^Fy9LtDt4+hHWtwMo+{!W^%7zJ@4PNW5!o1Po`E?-|b_rz3+UdsNRw% z@sSY2u17Hr3_)i-4eZ}~6CW%0`u8fuy}0w;%0%c<^8WfRuY^A}Vhq0DvzJZT z{GXv}ido5jhA-d$Gu-C+&v0*vyWu*4mxjky?v>hOFEnXJ&ddJ{FW>)X*e3a(VXr3& z&#SICPQKIo;_Aa6H8tDL&Hp#=Kf~|D`M<9$Gu+54mcnhsYihY>(?Ms&b#;%Ush4HtB`O^>Y3S^V>wRQ81W@?TLMM~~>NeRTW8 zmbMLCkt#EJ|1)fjpSxAxcZ1{IcUSK{UjKFLcQsSP3p^7{7$z@295Ut98JnuM*YXn% zE?d_1pTYXY$LUWNovAV3cHHPryV{JikFI62&VSp#^ml!w*Qt-vuS{k$EM2tyU)xHS*RQsJUAFh# z<(F^&%KvcD?Y+Azc6UR$WS_}Z+meuY79m;dy~pFveb0X9>Hq)2qf4GDQ|`Sm-E?p7 z<*bbh-)z=eX4&2(^WeJa{cCai?yvsO5dL!7lZ4BAUfs_uo8R?GfA!}73>LdWiocyo z{_|n)hMbx?dv(|()$``x{1>F;!pkYO$zA24yw6iv|5H9yuBY;^+OHP%JoVBdx%^^R z)Z2Rd&6CYv&A-}p_f_mAgQI?PmxNp@wL70v_v%{u-u>${<(n!Ypoxc)u+?1}#wO#ZFO``Yhu__w+7OS2^97w+$u)UEs*QN1_nmz(5* zTIH7}vUc;giS7L5*#1ksKchc?|I2!d$$OWlT0Q<($>z5F%e=?$erZO&3k4fE;r*)k zFg@emO9rRi`ex0#TlZ{v`Bnc_+Z~R*v`FcVHQBmL&e}9-(5HH%cQbv z*{!SlFW)N9{JGy#+xFCsr@xQCE$YAOziNHPs&y0F^5s3MbeVnS-?i+2w)|@DzWz5o z_FF#6btlzLzubQ}YI#ORsnnDC%NQrG|2*&V+h3m5W$*1T+o(tWTYm9hF5JBKtNyFj z$j!a0%oFo|5v#6pe75by-zUvqEx($T{x(*bNBB$9wB7wnZ=MdxEx0>znZv&vVRrv# ze;5DEG?TNByU@RI$J@)F|8<2`Pc1zCz5mve?XRX^oz?T?OOL_j#YxL{$L~DJzp6e= z>V4R`iR~|BqiWSAv%jcL%r@Ei%-AI1m+;%`zwJ-@aCdKuUZ(L&<%L4M`TJjv^jrU&{ro2lrAH0<8A>klCz`JRzxe2q|Hl6eyXu~7 zv;XdYXVUEZ;ddmzHZQY1V!Gz^(j$`@7(S#Ptv0{CE6;u3n_W@QUNGmTxZCe}`Y>&~ zAnTIm6Rq-_jsFV#XE3QR(ry%Wk$qoPd!>|zXReXainRNSH(e|L6+h=od*Fc`zs#Nn z+`9QC=60l-?b`X(`%NptZ{GaZQ9sLmr~c;6P$QzW9^d%8{}=bW{aXw;e;b}!d_gMj zYveA5yTAW4?ETZSKjXo*x^rh%@%mjnXSTU~_shEazh7~g^Pi#jKf{83ud4d^%(wNd zl9OF|Kil&0_ZxGbm;A}iIKA-cE!l6I8~-lT=lpGLf06&Q`*#(?Sw3bH{3qAnnAWx9 zoBiYZ7y7$vcQ3zUTlMLM+3uGnx6Upu-+6zI(C>Qx{w;sk^~cYhx7zIdg}?9b{)_ss ze&c_J1N9gFGbH7scn9i?x8L5)&wsh*^uleMUu@mBiSK@K`kg0-?Uz4!T^GxvdvUjJ zdR@Kc>qGm_o&Vze=i=X!wR7jcX#ctR_oR#Aa>xJV|7HE2c%`7o@Ajt^$8m)X|)*Zy36;@Qf}=9(aQkT>YYe}=pEMII^v4B^+V z1+V^j@bjN8Yx&>H-vzA^+at~)^{T{?|No`q^#L!ePp%1(sNg?Z-SKnl8Rzq7Uu=7L zJ5KG^rkDfT^^3kaAD$KS;YanXyR$EU`8V@Y{H2+5=j5d9JX?M16I=h6)!z))`xWk6 z$ycN!e{1u922bs}#j>?CK6YQftTlOt?T#}a{=TcbvcsR_al4#d`}a8>C;l_+%>BLQ zKf@(XH}}7%^Y5nrn-E=}{%i4nhNAd?wex>@>TmuV_@7~?{(lC$^M6$qf2&{L{ip2s zz4o8$lK(Ss6)G$Ld%FGI=Kl;Hs*>{Gb@!Lf|K*tbcmAvXzbET|eye|}@>u%S#49Ng z^;^^bP4d3^Z=e3htKS#?%bxL{;m{lltk%y@{k8nxyz`$yuJW>VU;1U&uG-2OZpXRf zu5Eo+sj#J6{noebpI19PH1RN)&awHhp3=m#E~g9)&+-``7M{c;_{dIAC#C+*w!hcv zU$Q)Yt8n5>hMm*-zX~0Z0Z)#fHM?bB{V_1&jkM|C-=AZDR3=;p()>gUlP5}F!MJ^W&eBmpZmYBKp2DwJ>gtA`>sIu_jQ`_O{;$$|Cj&w z6>>O<-E)!ICinpq;@BMd|MKw_;MaqG9XgY0F` zc5Q$6&+F3q{|rL$UoZb>n19VhbMt?O$^RKr+16|RXK1qjyjcJ5>BRLb*QtE@CBw;L za__AI+mTcPHZcCJy;o?+XK37OI-}`KHvL{Ly&tMM33-9l3Y6+FQ;a(N|xdgXrx|2sONnDse3clQ6z_|I^0_S`*x zyWXT-4y=8bz3?h z6fkLKo8>Z}nmL#MGX!}}v&_z2cRcL&%;@I1-gmdIXZ|en#H?1O|01WRW!>xb+wOSj zn(wb*I`6yYExCCt$L62ziRtWnYHuY zv;Ak7cmB_;DXN^B|F0ZNn;%mDdij3_`-Syq=daoSGkE%chTQ)QTLtPLIse=JpJ6v+ zb=7S9pZ4DhvOdlKq5Pl0%IH7Coqdx38O-W7{bzXU7yozrzbpHnoxc|UXXWYt4BP%Q z+}e9ql{@pp`JbD=PXEuKRrH_X?f!2|Q8ZkAm#MP+(D|$D*YvWUdl{Lyz*B86gS=_7 zLi5*Tj$X%N4*PGvzQw$M5h?%C|3AY@?PK!)89M#ue_a2c!CiC1+v$I2{%6>k^*P>L z|KBb9FKL&LytECsUB8)`>&>nkZ~JR??=Ua0PToIt>xusipVMFK{|kBgpW$}>+t~9z zmj7p%Y}NUnVR!v6t*Li4qW#-Y{jHDU>h;B*s&)kYsxsw=m>*vr@1Z$wstt3C%eU>0 z-gq!3h~f$Y4P9JcN8@~dHT2iXF$Z4{Cn?zIe%0CGn|?O zPO(+rIlOEp1^>T#Z2f-*A$xF^yUP4Ktn_!u_xW4&JXOu=Kh?ikaP_JE2lsy^ss9;v zR-ON}{_nm23^!Z;Gdzv|z4t%EX6{LzPPeM=!^HQW+W)=!KSP?h$|Nt4X#KWho9#0? z{=AH`H2lwCR%U$6&#>5h)%v#+-f#Z9IOq3X z{_vdR&#UC_-VXWCfH1Lj%h&x-YdPzu*1!Ay?~Ies)&@wxlyynp#h<@Dv&&$lms`TP5ShQOkiD$CdYXVCi3aQFS+8Qy~ z$3kFNLwxLCP?DLSSIoe`AP!RLTu_n+^8|x)PErw^6O>-;3+Ff&Z^TGx(&YL5xAOHYC3QtF^(&IS}!HqGX3i zkV#-HkX)2s5T22m7XV73nR)3T%Rw@HAc>&VC4(+Q8bc;SDnkxK3WF{~F+&MMB|{EFDnkl5M8GDY5a9F-5doPF zvIW9}kw^+)LNITD_<5x{ISdTok#4r6{L;LXVzk_xTw=(;03JzZ0A&GqI%8k}XBA|= z6FfaboDl?1t{|r|uodTICZ`sM<@kV#EJhT2Siy1(3=BdH3=HgGj;9Nf+3e{>`K1LY zLTvd(ndzDEveY#gWV%0CjDdlHH?g!N-#s-iwJ5P96%=?3A(aKNs(=fk7$gD;1JB%a z1yE$s*#FF>ML8%H3IizcQAK=<(@`rOw#1y0ki>KpReZ^*IXNMz6(yd<9wELypppd? z%N#Ia6vdnw`9)QZIhpCOkm84U-UB8K3hRXKN3x^sR` zei0}rp(Uyy#C#`rxCAJS{qpltK~-Eyet~~!NpUK2ImVTfng*)GQAK%@zy&0RYVIOX zF^DD%&Z8P2ix_x7$=Ut`m<`E~f?zfyBZLQ<(19dqMzAiBdIo`BX#QHjz`&5oz`*!` zfnjd~1A{;zg9HQP==8|w^vLM+$msOQ==8|w^vLM+$msOQ==8|w^vLM+h|=iv$msOQ z==8|w^ay-9Yjk>qO4B2dkqSldxJ5W|f=53fc-mj#PnPRIHZt82`Ti~3Uk?B!Y zlp0*+7m{3+ootz+WN)WnQ(;w+TacStlBiITo0C^;Rbi_HHry(&*a{@9ucQE0Qj%?} z1YUTm5aAo3;GAESs$i~XsGDqHV4z@bsh66PXq04}rlVkFU}UIoXsB;!qHAbmWo%$& zVx#~CN_Gl1MJZ`kK`w4k%ZgIcY?YwCDlaeBD>v55FG|-pw6wI;H!#vSGSV$dNz*N^ z%qvN((9J7WhMC}!TAW;z37%w80GXJWlwVq6tE2=qwj#FxZYVPo@;^9W3Seu9;aX6PhD(EE17<-*Zh@~ab`7wI%gu#bQC#9$ zR)Sx3F=#wbKPa_0zqBYh)wL`&uS6MSS%H;vQEFmIeo;t%ehw_A5c7Db%bt-mq`D-Q zq}nQh)Ss55Wl=|kUWTsVOQwm-(jiNNQBrzqiB$1%v zlw_;4{G!~%5?iIr+{E-${j7r2bR`9NI9h>6DSh)(Qf-xt!OrmYwerj>E=kNwPW5!L zRjSA>(96tBu}U*AHZ)5!H_gwH%u{3wSb!j z8PiV8K{gfSs+7zWtE5DuL<7q-bKR6=6BAuiW0MqJ3&X?|UDL!=qqMYSLrar115k1X zrw}WT%;J*#qDp862~I79vT`yNz=^^tF&Q+YXseVAisjT4C8%&Fmhd3TW~l!1jMO~5 zu@AAmI6tkVJh3R%F+DY}gkb8@$LaX|JkTIE!ubV>MTxnoC8<$#j~P9uvG%Miw43k~8@2gIA$LIWj%LzIyg8qknV&PdElPff8^(pLg`Mjupc zLE>2-Jqdv2kcv1v1skwXkc%6L2`-L7MYA2WTpguGLtr!nMnhmU1V%$(Gz3Oc2q5}= zsd*{3O65xSc7u3>Amb6HGiVzGbw@V{Qgwsidr|*eGwx^ADMx3&S@|}#b*t@}>^aiO z;4efcl-@bfyICQc)I#+*F&+OG3ZXDaY?AGhQY0qlK{xg_oze>LFpW#B?954HA z@t?~|W?%W}vG;g!_E9tLHM-yWy|d?ksh{*U&5@yxpL<5W)5#5~+u!&eU#>LgSh#a< zwDVevbH~4h{MP<%wS4!j?tJg+=Dl{)U+nyH?M#p@+uxaQYx}DIPOs~~_w32P)vxmZ zGxT`mSYHUd^yle+h6Q`2TGwryy7TJhE5)TebC-PldGX6sz23{mM9p@7*|w+q-Og(^ zj>WfLuiGqNe5?NA%{PC~zIuC2YE|*A;}H)JOQ{|?@zMUPrzZF27b0fQ@+vEJ*DdR) zo~Qn$I`&J<{SUMMGtB6#Vq0 z$|SEKPnAhiCV8qXRhi_e={X1lG=G^@+3gTI@V2G-@Z$2_zfN)dn8~~J={BZcx_PH( zSn3+S%l~xaeYH@K+2!Qdo38bz2JVXSty;7B%Qdc53HsGL z3z=-UZma!fscO8M_35?R1#_xaSm#x@On6)GcFnu|*84mE87^jRy0qPB--2V0BSpB_ z?zme_x^S0$`Gm(m&)xp3{Mr7)_AfP8ru=94e_`)`hQ4x#{|q~R);|mV&#;%X{_)%k z_V4WCe@?A`@ASFHYejYbre}NKEZXqlnuWo1X|2^SEvNlwSo-%!PE=vhyIhGM)At?! z!20I#e}=@M;1~8ko|o|d?w|fIHU9U7>HjS6GXH!0^gqMQ@PA+QO;4`BwD;?;yw!=f zvMc!tl%Vf`2zk7Br&)^W`#f8v1E=muJbE7KW`DV~ynfl=BU65E>4 z+tyRR?AIpS{mcF{xUG4Z{2=|wW@%w&t<&*`-F%Z6{yeW+{5H$0mi<42tp4i#FO2>( zoLT>$!F%O@2IO%3^yZeUWLS{aX8Gi8v(}b<-_l^7SDn3Z-G7E{7w=p!%r4sCq)Tsqmz`cS z**of*=@sMZxf3V$|7R$&oxeZ*#earNe`hRS(%bs%+0HdLa&$IJ{$`%*8@+eue8+9k z>3eTW{yDz#<*#qGGd1`BXGnZ2Z_Qf5xTc=sBuVLHL#Cex>Pye|)ljh3%ztVsAwf^sP@5{fsfA5X|&yaep z^7!7hdZl^U(R?Z$3__9jf7V?wnewWtKCr%g^?!ypyW-%!*mMcmrJG-AT-m<*?~T{G zZs!-ge>MNaq`A5C1N|26jemC>$@n@wwJ_J>{@}N~ynMRS3wFq^y-=&3TO`k^dF9K$ zMgQK1|7W;ave$ma{@qvqGkl9pUCFhk?7_7iZ}Prpl;q8OGUX*Umu!|ezV~2HX7QcG zwHYdt>Nv%0*YfEquA93~p&?P#st)dAcwnvBp`;?Ht$Wtz$mH!Cch9+@($=`Y zZ2s)+IKm~$6Nd|zOqy$Cx3HJv)#JzC3TFR1u>W)Je}+w`#g$+7zgnoWydEZUvi^qn zzuf)bA`?_5&Hc~to9REp+3o*MtHRSxYQrS2^8XBPxc@Vl-T$pK{cmIav)lg}Ql~L_ zYTp0PutERN?fN&H;=i%}XPBM;cUlDJpf`M~|IhW;>YvU3^ZKjdzJvd|i~r_7-*@ZH?-|t{BP;o{|ub*&s3KEXMhFVB)EYGqJC7Y&pdYg=(63L zK1J!&PTOL;Cb=`&-tEJ)dfoRoCe4Pc6CadT!UW z$4?(klV)Z}jypc#{M+2vx2Wbxm(uH)$GFG-@#=fO5RDn{aE;rc(y+0|ux*X8f+)VvB0EqD@(jQnf$huw6$<&xcDZzt-u*_U14 z+V`J<^XJ(=M?+Pu|1)5O+G$3WDewO?KvTLtERpNPC0cITE1QR zZTppE=?M7;`)#k;-~7`u3$Z|c>C#Rc2XChM-Lofw+5 zfVJ9tX0qfj^Stz3+c&NR?I?xS1n_9tAhy~{QdVo<+S+w_x|8l-4Bz{*ArMbZ#jJ9oBYCL(GpOT$`(~TmOlgG~?cd z(=iv$`*5_Kd#g}a{yguW|HjAz|9Y>m%dg%Z&wtG@!0z0C2C=2z%71KCwbFU{!{k}& zwclAM7E8$gA??$Jp{sZ{q#u^6v!hmGPf^Bs?DmwPR9?B4YaVzW+W z_s-D2`|?R@^5*Qly8E5h|2X}6@z>zQzwP4HEB76bEY8j@@5~-O%oZ9Kr!;bA;c%bXA*{}2W-iK%3f4}w^|9WZ&?x%&I)(*s;T3 zbj0$D@5c6JWXh*+ProH~_gwxL3==SLu*S1`%Ysk(kU3c?WeEDks7x&Klc8eW1y=Hk< zI_kO5MFt0zc_-qR{quh`9c;kg{hue-pFTOuOCBz^q%F&=mc2gYpUZua*o^KDg|}DS zOQyW|@@9@(^7T0HHR~^Loo;&7aJ?FXbnL!cW&atJU#^wf_1ypd>qzg}wYz)QWv3Wy zxnVfh@AStt|MJfE|5da9oIRK60fTgj;QdYYpwRG`|MWkDsk8snKl>|hl}E1K{a0+( zLGzspdw4TSCuo)3{bsq__Wncpm-WrAvdIh~-x`aZZ%O?rIMx=)w!U`#*ZPT@f36lo z3WIt+`Q@J`?e7=Adh_I~9qV81xH)yhuZSnYZ*(ge&z0T3l#-f%>;A%DZuN^@4!({k z4Y_tdfIp#tb?dtfzT();spXHVbMimW``QYQ{JPmc?Yo@)pWbzS@uApoe#?i`(!JRc z*$mS*i0AAsU%l(qyCbCTqgJiib?kby_q|6_*ZMrQ;?wK9gMW!JD>T&f)U!;vcf!`> zYMAP`h`eK&nI~2*d}9>z-aC74+_USue}0X6{c-hiiyPHeW#X2HSsOSe&ne8^`i+-CcSG3ywEf=MvR|g!gJK(80Q$|1{A(_@>`~C` zHH!`w?0Oz{&8BK?Ztj;?U)Ln>wVl29+UBaI^XrUn_q%CUzdF8V^OcReWxiXU+j#EA z_1jzb{b#uE2uelMZ{B}&W$K@O*Y3H6vChz0yLJCt(}rs|-kBY!o&V7OvfY1%58k%o zpdbNxGbl)9VjZGJ(v~}wy|;hYv?oE=t6vuC^5x6>ZQi-+d*q21{-rrDezdiThXW8IcpY8{q`}3cn{Kd3{MF&y@I6nVpNJ{Seyi)nS{hdj(cgngg?0<3T zw|@Qd7u`9@k(*ZDeHnJm@;`(3_j{WgRNA(9sykS!ECPj^_k|6TEKH^>!pk2_nwzyr z^W}~E$tNm*J8yrdo1B~LyE*SwPHOwT=Q;QPGcYxl2QJ$q*xvUzlegG5I69kgy>993 zvgvlmZ*E-sRp-x9my3x;9x6ZoGi)>m1>%%Wxvr1zGIMV-wHuk`C(k)yy!+>}8{e$| zZ9(#@Uh0-AlSj!r_O0T+lUs0m@3$QOTlbgT`gS!sG^Fg|t%nNR{PgPBoNuFMwDwBdHd8$kS3#g_qzZm;{+6~U^s90g46kYq9H{xIX zXLu?9XVN5zRKXyg_@CjD{ZEzmyU*{5 z+<#H>KSRCzZTsKHc~y7*w&wmT|5y1x!zAx-W#Yf=f4To>P?>)B`8}=w3@+(^|J3LF zXLv7TGUa`8WNmLo+twqD4;Zz$gfuSCRXpV5?+kLO6Wpa3PN%=Xy!8w0uOIbvgHWpW z|1aYXS@SCQFVpLf&Odeag3&5g(VYj6ou2b-OFP@v-DUm7-m<%2)+;8KSj{ghShjKJ zH}hE!x`V;1J0i_wK&pB!Q=kR3{nO z1lg_Rh;X{2V{UbcwKtXR*v_Soi?N?1to1a-AXq~rrweS3}x~-;)@>-!sQ&&3@| z)69h*7PbF5__}`QkL&*#mR=2J@-&w{|I+pF$tJfVrHB`k++P>g-`{h|du95MUB3=| zYd2dd+xP1Dzy6^A499N2mj9*yE42P)i3NYfe}-Ps{pIs(>VM?P|Bd+6skik0qt~yQ zrD9$!;PZR+`#;0=3;!9O@kjqXe{BC>z3GhaZ@gPm|KiIIhy}IxYCryGc(|kfYw~H8UHkv!?%VjEVbOnv zrQ!dk2bP0ecx<}W3g*eX_Wfu0id|Fu&*p3Y8Hz!tlXx#hwi45dvnso$p}OO%K8lsz z4d45JTnxYa{J#7@>GuB&VK$4rS7JEB9@)kN)yMy2tbX_TegA(3-oyVHR?B6oUi!~) zMsEA>`MveOVg=`eLhY8!ttDmFtL1;$>M`H5f9x8M2-uT)>;E%I=dY=MvHxZ0e}<)3 zV*lyw+hDl#+$x4A@7C}CTZ=`<^nVFo_uu)G{h#5|%X+v~=n6l3>McdH-5bTKOAl-x z|1$~wUitm_e}=Y){~50KYfO5DZa+VgWd(8{|1(Hw{oeEY@qdQH1^*dd9iQb{vi~#t z+JDa<$N!t|f|jZ<19X%3YUGqwDh`Sep3LG^4VKoc-~VTb$EIcJe});h`QPoOKx3`( zaN97I5=z&YY2uX1uIcDba7MN+hJ8={v95jZ?SA}c_;APm*T!d)V)YR@?4l$nx@;Go zUFDv0)T)<>mhwX2|AzIQY3V`O_VY;$_P85c{LYud#FH23ZS$4llD`j-0GYs2|bS zElWRl%GfX$!|5*D!!}+P} zBmL}k>egKTmoEOJ{>zjt8|xQus`<~LwdBIBTQU#zthO$1{Igtp$C6#sc6>_zH07Ge z3CsF3_D_F08S%w`=Kon2Flo+}NuH`!5%bR&KmX}v#Haq5=jXqmo=Fp){%0`w&oKQd zw`pc7oT;jrnN)9Ze*RN!Qz%1I6J}CR^YlN5|HV&HS#GK_$CCt@KUHb3Xi}J&^W0H^}*(_X%Me$ z4hotEwRLkClrd>C+#C*=*S(5^JXNN=gm^^bgay>=Ud|Br>s(z5Wn7uE6l&|5t5C+0 zB`{N9-lk^&I?O3QKK*IrYHyWkm!31Txa~JsX<>G3x`}h!x{Sxa&U$)GpIn>$pJBRD z{bQZ~4CklmN5=VnUH>oU=CvFPxBhQ8JR8SJj?-D~k@Ort1p}jCD)qF*>*%`8VPG{3n{4dtY4s*So*K`{MGy(sd6yCV3TalKU3(Y~#@d zE~c9-8Tqg6{(krS{P)37135G`?X2(po^^f44#{b|{25m7>$QJz`#Y<<@Gob(`Uz`Q zru0KhTA5<|X6M=DZikNSlp>~!kvr=z$$$FOsA}+a-QVxE&wpPuy!ZXiwY^6VoY{J* zf|XGkqJPP~AMdN*Wyd{wkQVjQhH2d|lm85h^Y4ht_`l@&`Oj&~q#%2UNlPbu-}KzHPnSt? z?Zgw@u4zyIGkDaW{=_onFw}=G$E)ubM|~G(HoETlK!ha+V!))?_rM-x*(+6lar--t zPL<`>$U!ae7ZSW*ZT7$9|5@kIG->kVz3+Fft?q7|nZ4u#*KSDgh7{T_F@FA&>yoF( zU$8-3)12>}o^#D&3rE{FcLiP1+0*|j{%6oXAr#CH^`Wrr``WYFcVc)>$Er632yTQJ z;FVtm@uAR{%>N8-_jf3%Oq%nieeZP3HH8_1yg7Xi&d>n9^65Xr68on=nYg_C`@Zjc zw)LGJTk5*W2ejTpLO2kT0<=9FteMVFg zT5q;$_KIZp1FX)FB#B7Ud;DLw@UO9KZqVDnVE+N|K+FUsWR&jxW|j$ZB$?Vg8%Espg;)a>Hq&4o)QbKlA861OLCt^>45IuzvTSAxn5g zopbLQ1=Bn1nFnq^ShZHi|C!|HKTAzp*M5`U6aD=^!!ymB&L8jSRkE&qVjkyq?_r$d z>3@m;8T3yH1@mtYe(-+x-<3S4<~+C{t7nz@@2aEU`sa~<6Ms5+^A>e_(%I0>E}O9nbNp} z&Gvlr6IIU{XO})Y^jCU`d;j*M(M~6%R~5{784kTMOwL;o7$MVt@ZL{OsD?{a1EA|D&EsvqB&5oBnH^ za<-!ie;4Z4r)FO zbzbjuq@tNKuCbq0HQjf(yu0o{!=Izp8g@rHU&3 zH&0)&fIsEj^Bl7)d9P30zQYwY?scwAxepa-K`}~ewKvoVPv3l4a`HPR-}kWigl@j# zu1{v)xjsP~DU)VEqre;H?FEy(a#2zcCp_FEkNiuVZ+|LqQn2e3d)Y(JCq&(IE`5^w zPOuJ=N>;*}M%<>V=KmmKsHu@_)g_1-LBY|mmI9@+zKXsIld zhvd#6s3%vbOq%Vt@F$obJ6+)7B%9M+#{KpG+J`vkK;clgLbxS-n zxGP~TJKz5d5TVt_>UK!}Iy}SuS9bm9r>n1CpY%7^_3>n}F3pLzNp z`(JfMl}WydSQmrMzHv5l>5>M&y?ainU8{TXdy3)y8TscwO$hx4 z<7UKvhJ#sg8}=-IRsT<8!+(aytNt^b->RPttv}S|r`d;P{%0`m{$u~kFA5rTA&{CA zwZc=|uuB7)))u~w{r&Fu`R|L8k?Tz_hvng)*ZkY?XJH1kN*DUF>WSgz+YXavJgzso z-}z^$WICjYG3SC3a|v_PqCeepMia4jo)?(%N@5n z7=7+R<=yq^8SAP-`=l=3ZkROZdv#WJ^`7bS4d1r^`?Ed?+$z?Jo0>k&0LJdS|aB*s0gw-yhSQ|J& zK`f&Vf+bqIh0vb)uQU4gPPxymBVRggeGW7q>x=w)_@ALm=lqY?^`GCaJp6R}#aZ%a zp8jWG{x`X6lBe2+mFR66&^W*EpZsUipZ_csWtTiFvPFpT`u>?Nch29FukxygUNC{R zIbBJr7&h1W zlT2PBvCnfez{ zO|BRWtJ_81p!jgo?2}s1CMc|un>6btq+iDxh0-QMw6eSsA)XDJva*#u)vngTU&D%|8LGua_EsT-_D6bha-$x~%HtQG9$2x)T$P70FQ|4jbVpM_~_ zb)bwTOLd_Zr@=aOs%kKE1SddU&@$!Ol1ZML&<0rG1bB0^9b$9jYHuiGrKUH`+DJq* z+!Jn43e*LvmMc{zd6htF_$eteu+E4gw8|38x&&u=1wkzi%Yrh3f?(!ILjCUPm*uHK z{XqP-;{)dpmMJfwW1A-|4s2^{Qs}(!2NaeQc=cG`N@YJ>wdi}3nXy~{o~hM{P&m17 z>wkvRGwnZu+SB4|)~o%x{+~fFz+_NuH*y#0^N} znF=>v^*f`mH1F0Ug&FLga*ER5`tpDI|I+=>aAMU-bxw~Qk6)Xd?IQRjQs%g4oC=ix z9Bsd)xc*Z8#r%J+`nN3izqEg`{Xc`!YL)3b?eE!Vz2BSupTTFOs%^yDDe-*ui}`O&$doKFH7OY13K~!+2DAhSu~-RP zH)kMKRPAf{&k!%FDg~icK;t%7p6pets9%3?`jYefUw>cQ|L6XR{|sK|kN=+ZpJDb- z`InKP71Yyjoc`Uq{`Byjnlt#?fZH9Kf|2viC|@4AXaSs&j440u;!c5zlY)XE$cr|EV9M!k}2^Q@{?sg zCv#-zDfk??evm&||J{_n;^``kldN7mt@8QspCNv)KG<0c|8AH5Syy*a{^v*g+lBQH z?a#{p3UoVn{!)7TufH#$N}b>C*#BYvv;Myt)8T4Vjlax%_-}tz++-X3>+b`8=js1Z z|GD@-gO}>JJMlmKKfC{%;_34j$$eMsZg>A@;QW{IpW#wx?RN2h!hbIQXILr}Y!7w_ zG<^8CJ^IgZsD76HmqqcpNB=XlfbNxh=`v;1L9i4w#6sxB)yJ12IP-RWx4*X<5$hs1 zuWXFEaAU*q3I7>RE?IW`=8C`fkNjt-iceeoy z`uk`IL5oqOG&HSv&ULXZ)3z>gXneq5u=rKv@8j>}?<~qcrl)r|LH#H5>3@Re>wAA+ ztDj;2Wsz6?|EpGKs@ESceOJHoTRXR`^W8*~y$kZQZSAYIHm&;4@WA%B{hZBTx_ucE z9(D81j(KifaO?Tbv(o$KotpPNJnsDuGu_N1b9al+IIbwYH-B;9-Jn;I?rVeNo^Ne? z+gIIPK6h>XX8Fr`?|+0}nD{EvX2C6**4qu?W*?3t!wh$XY0A!>T^kP z$2mLXNb4xHO?p-}f5~697PQn}Q((0%hSMf_)29$QvB_kGfyhJqD# z^ByO+*1K5Umf>VCe<}6a?Do6=4BmTXt!!d_H~Jj7&Y)bK8J&61I{N+6PY&uo=RdXn z_wql(+wK3v|8-?cZWg-`6Z72k=sDYy@?QcYqa$J*^@@9T9uKPjJjH%<*T*%hR_YcM ztexfTDERXF@2&qCp6~y$gu|q!>xD<5`$8eH`pVB&rcM8I=e7NJ`G0fkf12!n_B`@G z!;b$9Uta%bIJxG0w)#8oKS5vYD-ZrFlYe~ltY>EZ*>ju!GZg-3czGRE&i%_Po&UT4 zKZD=?KeOVl`QMrTV`==T;?d9;O&g;*Wwa<5Ef|MqHSz!2qaaU}0IV~WgMWMgvuEo zJ0BcVXW%q?__e;wkNu6_>VM7uX4`+ttUL5-{|oz!@hyX`mie}A8dFh?J5`1P1c z@x{mQ#b@omwDUhhnS9Fr_xIe9%rV)IFh91)v-Z4vW$mg_cMi+&lzS8XV&|1xXSW%d zn>lte-tB5%_Md_O#FkzA|E!+)xBpk$s;-{nJWmBLO#Ei`V@Z0Su=P2^iT@cm6u#Vl z_)`c(2ww6{aeTS|@F!Iep}b_$gsS`xe>y=#$4iwZa!u;J6eWZxvO z!KE#?kA=z9Cd*ozOYc|i&wnmcx-ZwaKk)_;?w+d19`^LGHUClPvKK^@ObX)n{U<+B zA4JHiOlbzGr;(=M{}&#G$6ly^vdVt@e}=HHC)Vv=@SoviNPYglwXd$L|9SZDW${1l zKS%2K*}piuZ`Iwa28M40pW%tP&ZO1f&&*x?pTYXy z&kO$+fs- z6>a{${M+$=A7B4x$o+Eu%l$9%{~0EDf4lGVpTQSgg#Khb0dwi}e;X0{FgTSd;hone;;4}hw$Na zgvYrjKphGRE${a;t=->D|NCkG#R&VV`keara{H0}$Fd*c@t78fOEn?E6<>4mmVL_p z_f!9M9sGUyck+LRim(3}5dKp|_V*K2s7qfLpMJ1of}=t5mP$oXSbYpP=%d2{X<4|Mew90f){Qr56694{)U_gog6s*Z?CH7>dGYRbR&nO=MHYGmH z)#;VrtGfmXolVg{5BdhanExr&{`P-{)m3}eJGlk)^at?YdXm=A=6N8O(@Xh}q|Ns5 z{|r-$>^J^r2)G&;{pvr%r*-b%>|aT((%G~Bod54jReI4sd*a_b{LkR((s(LUPIK-} zqhxJOIZcm`fdN~OFm5@hX3}T+(bF@fVyW`caFl5E^z8(x3w;U=byy+|PIB)GvI(t( z#2_rePq{Ji(UgACIF$6LYNkR{bMJg`-!Z{Ms_~%m0cDkCK{i1XA1#{Xm8yJHQ%*}0 zMZiMg!-BbcF9oZL*P8wL&!9A^d*AH3`iZJW9urumOqS#F3h5VFQn6ymq?sNcz1+K+ zPx2H>32^>wfu>Wh9I)_uNP>qJ50hpKfoxoT8d}akLQiGc5s*=isK=-u3@Z&42T6;p@Ni-)ygXxch9}?Cov;{+->uZC&f!^raU&jwQuq zZLya+W_k5y@~l_&b4vfM?O*vj_Rnp@+tIcnt6$s7ZJy{G9g!=t|7ykFEAQ9b%1PBt zeRC!?eo5-os{Z>()LhHIv&sd1mU=YiH(%*S)%zU7ch1>dvX0uP^WaXJF6! zrk(SuR`zzbdEA*S?%e+KU?b?XSpHXdFG3@*_=`gf3?a# z%}#%o8+Enxjk)E#-ElH^$~Rv%4bKpd&fcWI`OUV-Sn=>=*_U;b|J`;y@%pa3&W+PE zYb&1qIG3HhZsE=;hG#ZSH=Pz5D*bljx4Pc`{|sH;KkIDle+liMkol-K;@pWfJC9%5 zxXre2?cso1t0l$k=Rdt3cmDpyiEPApj zO6u>zg9q0u2p(iEhz-lXCiSaM)Yf|Wy>kl_R$aS0=VRKlUH32iYrd*e{^Fy=sl9K$ z=>^@B{+s{c$*$FltqhGel^kDodO_naGs`1FoPQ4fnQ}EL_15)$t8z9@&CZ^6+`^## zP_Eqhx7sse9g<7-^pr)sdbjPDj&zE*ws`(`^Pl-^PbdB@ULHB?%(9f*eP?1$Z>m)@ zJQ8&4-HYkMb1m+sH}@~!^%)w+q! z->&K{+qHJral^f~Kh|eX;%$9jUHQ*{>HB|9@wfjol>cX_{Lf%(760kq`oEv&cV8-c ze{xy3#Gk?m*R7V{I3)Gp+OG3yz4yP=n+C-u|GMwC{&FtA+O~VIqs(r^NW98jx^MIQ zO>e)yE3I3<{FmyiH#8PP*Rr z-EV(l$)fkm?$3YX=`&f5tK@joq#2(VTq|MF>)XHj$6S5RpY;)unfhC&T=p~VxyL;# zckBC0*WRz(H~+={X&b9@Z~XY{EcIvFY>idd?2hqyM>)@Zuq*r5egA{2&9!>2ea-&- zE_-HwZrQa{H=bAhlv|otcI|z!=K3#TzrQK}>Ax*n`Y-xe&yO4+r6>MDV@5t zx8NQ_b@_(Km$x^(`Ko>Q{jJRs$8UVwv~Bv{BWzn2O!B-K`O309J$~ZM32C=auc_e> zdil%#r0a{FC6Cf}ez(19c53a4n1eg7?mA|*?bfx^b$w5ix9zikud#OG#m^bPwR!W) z+=62s_DR&bP22l5_siqD_ZQ#&(%QVyXx7`lWd|PpXYkL=ZoI$z#g2c)Ke=n~Ml-)T zbMSX+>v5jPGI#sF?T9NXdpke;&A0Gh`uY3+@ml;k+!;NO`F{4tRVyCFJv%&qVzAEQ zt0mKO({69Ce_`wKpF#d${OMN@LR)8+RGCDkOkz7`dCA56v-^8X-Z!=RI@R8Cmz-xk zaAc|bbpPDz9eHV~6?wIxx1A$B7`D9M^l|2-`>%e_5C3#C>hx>N(zNv5O54TR!vpjR z6sDW4dcFBS151|F$|aLLm0D4v7j{(i|4Zc7_{Dd_zm>n`uFa3W^6lNt&G{Ge-)mN$ ze|G%){*+nsx&GC~-?9I);K`D;{~0FLzdQbyW&cV0@5%p~7QEg0E#qfX3ZsJXK|3$w zC+|P|f7jLjZKSP=Q3)O#~{~316 ze-R3v4bvJ7HfaDIFtuu5LI;Z{OOgAe3mD$G;u%dOqE8cS$TVmY8zh#S z2RSf&;#<(i@yR{xKLaF(!2GXjDnx;p@b+C>pI=_dkQDsuGrjcWIMvUZa%&!w|NZ%&;ZkMZ`u_}CPwJEZEnKx|`_=ypPhHGM~A3C#xA@U3E#s8cDxpAJLjE(HoO%D_+y4wJxOaxgZa-iDap`}Cv+Mtz z{?G9KQ2dWe{~4ZL|Id*6pW*w%Dj`qJ&-edi)_>0a&#>t~!|xB%|C#Lnx%EH8&HBGT z;#wwo)qMZYAQk`T)_;aK`~Uq>{m;;=|1Y=xTl{~9I`M@nljht0XE-+fKZDu+-}?U< z_H))FyXA$a%JNa$VIlvZXytD^-z$FaJ>R9h=)C1!UbL?vj{U0rQ=R=ke%rsgGihG) zqWY6l{xg7PxIQhuX#eC?J$UM?GV9U5Jo&r*Pd>+-|NOIlzx^+NU8LGI>=%6gvfEt$ z^W=YD<~2|Imn{CqRd*vAW`fr)Z^IzrM|B`%rbFH`T-RZ&qbnQR- z-2c&S|9)l9y7Nd8?~NSi`c9szcGbucFN6~3+!rQ!`p-j2g)RG$liU}TNx{$eqog~1 zj%YU7|u2i=7NE-B3anfzz?xdmJ>{ETawQ4|8e<0 z!?VkfQt4?Gm#60E_kS|~Gkng36ic6`4>IdPrxMVhrT%{ZQU5{hy(K^S`yH?~6Bn_5bOu|IhCHuk9+! z7a+~`tw0(Dj@q$*cKi3_pElQx|NM)8fBxU()1~!4!~^OYI-)|7Q?6_n)C-{l7mu)4qH9Oo{z+`}3bJFX*y{ zlZFqL`&4LZ+I#>3@b(5%WKO{m*c9YxOBF@R2tqo|aDy zJiYwoI450H)t>SpNM^~TX`VLA&`zONndIqDo0%@O=s#HB_4>u~WocE{jP_pd-F-*e z;NtJon`ONjW=M*jpWP55yX}(K##a`+&y_ZRD6pSs@k&C)|CZMEqi^Mps+tLfx(6Ll zV{v;)Z)g+V?fK>mlV*Er9bfUl zd`{Q-g9rY(JYJD7(LQ5!y!e)SktsJkL*cgiRrN7fo>=oohX0AD@06*9R}4JnZwX!B zeapV9<5dL9ffMU$?l5`fs;riSTPobQu*v1`qO~<~7ykqWC#r^&>M$jwgxlpYS|VJp zY8DiFJm8>G-Nmqd`WgSUlvgGQGb`=(JV&^G%1zI3S!b5c->y+V_AdUhqHk$~4^P+gvjVJmg zH~%w)J$;uB*XH_gE+Vg-)O=xum$*iZlc`9H&wPj}=0 zg`NN{H3@aiU;Urq)K2*u{~17nO`n9aNO7XOK{ zo%%P0f8)Vkkx0ElhPtd|?bn@J%U32avn%cVRG{U)BB9YfW3@l~mj4W(_z07AXS|aV zW~ayeX_>7zgM_8%`N@r*zDuSSUomi)za?~e^R4=>#481|$6S*iJ+RmE%2~2%-qHph znXcz253H11*u?U8(OMh1%i#EkD3xI^oEY(A2mh%VUv(G~o`l=^v8tL)3Oyfikg4us z*gXEsf12%q2g)Ty`@0+dtkm2zX;sxU27@B)`RYtds@gb&zjbZ-Q0)(n4^UMpWkFdRIwu4WF`#8(mu{*Pk+In-2KPxpi zOj=jf#-04->+1&(>bnZ(wz%|kE%>o_!M~6xb5&zXWrPyq+72v|7jj869ifM=v(v8 zprDeeg$R4)7B=1ZYg_fhLjKXD*_;YIZ*Mm9w%1uci-ZF>gjnY^f=t!m9@LM|2+x)S6%G>b)KMVf*@t8Wb#1X+Gltf{hmg-?a{3{O>CE$><6lJ@^^0ss&>Ex7;y diff --git a/doc/qtdesignstudio/examples/doc/images/loginui1-entry-field-styled.webp b/doc/qtdesignstudio/examples/doc/images/loginui1-entry-field-styled.webp new file mode 100644 index 0000000000000000000000000000000000000000..023972103a2634a68b06ef8f0e04974babf9b17a GIT binary patch literal 10894 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 S?f7PY|Mtt;xw<_J3=9Bnsymhd literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/examples/doc/images/loginui1-image-properties.png b/doc/qtdesignstudio/examples/doc/images/loginui1-image-properties.png deleted file mode 100644 index 88981ca72dbaf409a40351d4952194fb5e5a02cc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24896 zcmeAS@N?(olHy`uVBq!ia0y~yV4Tjtz~sWg#K6E1>h{l#fgzFI)5S5Q;?~=}?BT(; ze{KI;)B0ci!rOK3m$*6iZf97iytjPG_c)hTU)F_4dj+z%WHzn1vGUSv1&v*I-&LKf zy!T!5y!E}QEq*Q=7X4Ybu}wKLJ1y<$yzhIAp2u*1-miAxNXyQhKN%SgTtE50=}>|p zLxN!h4+BH@!6XKTh#cE>>(+&@j}xu#xRIZq&)?0sVY!B$9v?qHf8@fA8w;~%{`pg5 zYP$7q^qXH&^W*HUb939D+ig?#=f=^aOSf(lk!|nWuh75K}t5&VrZCh0! zd*t$$FJI*A{}g`BNK1QmZTbX{B~49CuC7O8BiCH`Q9r}yaq5i!hk5?}(Aq7RlM}N; zY2$+jhx)(rH8(f6oR~FxcKCOJ{?5P~2M#c#O<|~s>65o#w_t(7w+THxK4D?kuKiuQ zbg85hM}1~?_WL7AEcXBZ7%Oi28g-}hXqd?6r#IO|-@1h7ZZJJZsHqo& zoovhgM~@#@+T433r>nbm#*7)han-Yr&Y3eO>6)Od?B2h(KmPpl?_b@c`>|`g*GlK+ ze!6nIM){WW6hl5ek&Ukop4Gpdv2m@wCXX23&yKmfcb`(2<#BIkae8SMXE)=A&L2;w z$9E`RdU&|K^SJ%rFTxjVJ8#~<&6U4t2FIhVM~c2#-|v{=A+*>a>4e*!yXTH2&DIO7 z>p05QDy~?by7AD~*VC?dX3uEqd=Y*9;~ZS~$Lwm4|z@jlt==g|UOzpwBAr#dU@T~Tb#>Z9A{2Ch5nV19Mu&7|3JANoZ9 z=l139T6yT&wX(myqDL<6yK!4T0hBuJ?Cg%*vf1dit#;%0%@T#PSqlp%M<_?#{d`bN zc>gZ_Iju(F-vrD9bfycgyCI&p?%}SCO*;}x>rZ=gxxW7OI;-}&M$ZoYoYzJBUVQ8J zH9vAFiJ``YPu5CBSJ(H??~A%_J%>12JKYkkx8LZ?|8?uljGQ{}7mpZ?%;Nd2S8LYK zE?lqe_jPyaR1OLG{QfW-jeQ%Y6~1dexv|v8=l=Oi{{?3<9{9KUhJO|7WAE7Gyp}0K zGfzzkx;SO&Dw+BpkLsqi@O+$v3zbH^~yWQy5z;%(6jflcG*^)oT(-heDH@u*QJA3y`Hq3 zIOTP@uFgC0Y5m32C!GQx4n;3p&%Vk_uwOKyonOhA;YPXG?>P1PPnkPE`e#@3Wv1|) z51a9C&!;)Bu5GB$k8)cwV|8U}e^uY=E|X)ty9|S-M;rewxSEuwUBCXEMOE&@v<+9v zLQUCz$4I>AntChAaJz5)mw($1Ic3QC8i(6voZw*C$Jj0S|9$CUv5x=q>wkBh@S8sG zZts-1?d^|(?61onima^*P%V9t`E=rZS2N43iMCs8q#m=B_RJ~Vuzp6J%CV%l>*Bkb zLbtCy)w_1~_a*kRpMU#`eV(8a*y{18qTp&$5*H{}R3~rrRjOWoh~ve)@aNXeD%am8 z{@iA__nB+_ZAIbQ|B-8LGjFQ#tbMcap2=5vz9Q#c$Ij2X>~lT(Oz#d6`8r>JzP^x6{H$!wNl}|`TsyQug5my?o}$nc>%bWa zI?pOHA3Cmi^K0V_W5WinSo4M;Kgl`Px!a5y7S)`$F8p356>qy|@A@lmrq90?`Q~f< z`f1bRboN%*+24wMq4N2!^=c)RSNAOLJvmqRy(h>gF@mQd|56gmJEyXUi;BHxCeAFH zJpcOJKpalY7H9_kvhY*NYk|52|a-gh0H zrQG9trgg?lN$p_%eOnE5*cf!St&!SQ_~vJyyYpfHU++s}^opkcf2ZJidH;%^?;d<8 z_&@j9oyA)1txqHLmg>5GV@i}s`8+q4XK9^++`pU|cS;NcKJG4cH~+A16^F`Quc+JB zS%rN^;yK2 z84e^dFfep8G90Lm>S1JHh*!tR_z?AQg`mQq;pl;P2jV9;?B D@!zFVN}pfG+e^X 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 diff --git a/doc/qtdesignstudio/examples/doc/images/loginui1-library-assets.jpg b/doc/qtdesignstudio/examples/doc/images/loginui1-library-assets.jpg deleted file mode 100644 index 4dc8e4e910c0019c5e3e05d1d0ae0895feb1d82d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 68266 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 diff --git a/doc/qtdesignstudio/examples/doc/images/loginui1-library.jpg b/doc/qtdesignstudio/examples/doc/images/loginui1-library.jpg deleted file mode 100644 index c5f8f1c4111bb3d0ab0b3c49811841ca2d06170a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 76710 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 J1@*p+3;^wPgC+m~ literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/examples/doc/images/loginui1-new-project.webp b/doc/qtdesignstudio/examples/doc/images/loginui1-new-project.webp new file mode 100644 index 0000000000000000000000000000000000000000..b9a1dd8ab56d319ae78c96ecc8d2931c2ee600f9 GIT binary patch literal 31784 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
    sw-ou-2rf~=RUj2R5>i$fg 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 literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/examples/doc/images/loginui1-project.png b/doc/qtdesignstudio/examples/doc/images/loginui1-project.png deleted file mode 100644 index 28984175b94f76dcb086e20f7ea2480253d986d3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 35138 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 */ @@ -5898,6 +5916,7 @@ SQLITE_API int sqlite3_create_window_function( #define SQLITE_DIRECTONLY 0x000080000 #define SQLITE_SUBTYPE 0x000100000 #define SQLITE_INNOCUOUS 0x000200000 +#define SQLITE_RESULT_SUBTYPE 0x001000000 /* ** CAPI3REF: Deprecated Functions @@ -6094,6 +6113,12 @@ SQLITE_API int sqlite3_value_encoding(sqlite3_value*); ** information can be used to pass a limited amount of context from ** one SQL function to another. Use the [sqlite3_result_subtype()] ** routine to set the subtype for the return value of an SQL function. +** +** Every [application-defined SQL function] that invoke this interface +** should include the [SQLITE_SUBTYPE] property in the text +** encoding argument when the function is [sqlite3_create_function|registered]. +** If the [SQLITE_SUBTYPE] property is omitted, then sqlite3_value_subtype() +** might return zero instead of the upstream subtype in some corner cases. */ SQLITE_API unsigned int sqlite3_value_subtype(sqlite3_value*); @@ -6192,48 +6217,56 @@ SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context*); ** METHOD: sqlite3_context ** ** These functions may be used by (non-aggregate) SQL functions to -** associate metadata with argument values. If the same value is passed to -** multiple invocations of the same SQL function during query execution, under -** some circumstances the associated metadata may be preserved. An example -** of where this might be useful is in a regular-expression matching -** function. The compiled version of the regular expression can be stored as -** metadata associated with the pattern string. +** associate auxiliary data with argument values. If the same argument +** value is passed to multiple invocations of the same SQL function during +** query execution, under some circumstances the associated auxiliary data +** might be preserved. An example of where this might be useful is in a +** regular-expression matching function. The compiled version of the regular +** expression can be stored as auxiliary data associated with the pattern string. ** Then as long as the pattern string remains the same, ** the compiled regular expression can be reused on multiple ** invocations of the same function. ** -** ^The sqlite3_get_auxdata(C,N) interface returns a pointer to the metadata +** ^The sqlite3_get_auxdata(C,N) interface returns a pointer to the auxiliary data ** associated by the sqlite3_set_auxdata(C,N,P,X) function with the Nth argument ** value to the application-defined function. ^N is zero for the left-most -** function argument. ^If there is no metadata +** function argument. ^If there is no auxiliary data ** associated with the function argument, the sqlite3_get_auxdata(C,N) interface ** returns a NULL pointer. ** -** ^The sqlite3_set_auxdata(C,N,P,X) interface saves P as metadata for the N-th -** argument of the application-defined function. ^Subsequent +** ^The sqlite3_set_auxdata(C,N,P,X) interface saves P as auxiliary data for the +** N-th argument of the application-defined function. ^Subsequent ** calls to sqlite3_get_auxdata(C,N) return P from the most recent -** sqlite3_set_auxdata(C,N,P,X) call if the metadata is still valid or -** NULL if the metadata has been discarded. +** sqlite3_set_auxdata(C,N,P,X) call if the auxiliary data is still valid or +** NULL if the auxiliary data has been discarded. ** ^After each call to sqlite3_set_auxdata(C,N,P,X) where X is not NULL, ** SQLite will invoke the destructor function X with parameter P exactly -** once, when the metadata is discarded. -** SQLite is free to discard the metadata at any time, including:
      +** once, when the auxiliary data is discarded. +** SQLite is free to discard the auxiliary data at any time, including:
        **
      • ^(when the corresponding function parameter changes)^, or **
      • ^(when [sqlite3_reset()] or [sqlite3_finalize()] is called for the ** SQL statement)^, or **
      • ^(when sqlite3_set_auxdata() is invoked again on the same ** parameter)^, or **
      • ^(during the original sqlite3_set_auxdata() call when a memory -** allocation error occurs.)^
      +** allocation error occurs.)^ +**
    • ^(during the original sqlite3_set_auxdata() call if the function +** is evaluated during query planning instead of during query execution, +** as sometimes happens with [SQLITE_ENABLE_STAT4].)^
    ** -** Note the last bullet in particular. The destructor X in +** Note the last two bullets in particular. The destructor X in ** sqlite3_set_auxdata(C,N,P,X) might be called immediately, before the ** sqlite3_set_auxdata() interface even returns. Hence sqlite3_set_auxdata() ** should be called near the end of the function implementation and the ** function implementation should not make any use of P after -** sqlite3_set_auxdata() has been called. +** sqlite3_set_auxdata() has been called. Furthermore, a call to +** sqlite3_get_auxdata() that occurs immediately after a corresponding call +** to sqlite3_set_auxdata() might still return NULL if an out-of-memory +** condition occurred during the sqlite3_set_auxdata() call or if the +** function is being evaluated during query planning rather than during +** query execution. ** -** ^(In practice, metadata is preserved between function calls for +** ^(In practice, auxiliary data is preserved between function calls for ** function parameters that are compile-time constants, including literal ** values and [parameters] and expressions composed from the same.)^ ** @@ -6243,10 +6276,67 @@ SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context*); ** ** These routines must be called from the same thread in which ** the SQL function is running. +** +** See also: [sqlite3_get_clientdata()] and [sqlite3_set_clientdata()]. */ SQLITE_API void *sqlite3_get_auxdata(sqlite3_context*, int N); SQLITE_API void sqlite3_set_auxdata(sqlite3_context*, int N, void*, void (*)(void*)); +/* +** CAPI3REF: Database Connection Client Data +** METHOD: sqlite3 +** +** These functions are used to associate one or more named pointers +** with a [database connection]. +** A call to sqlite3_set_clientdata(D,N,P,X) causes the pointer P +** to be attached to [database connection] D using name N. Subsequent +** calls to sqlite3_get_clientdata(D,N) will return a copy of pointer P +** or a NULL pointer if there were no prior calls to +** sqlite3_set_clientdata() with the same values of D and N. +** Names are compared using strcmp() and are thus case sensitive. +** +** If P and X are both non-NULL, then the destructor X is invoked with +** argument P on the first of the following occurrences: +**
      +**
    • An out-of-memory error occurs during the call to +** sqlite3_set_clientdata() which attempts to register pointer P. +**
    • A subsequent call to sqlite3_set_clientdata(D,N,P,X) is made +** with the same D and N parameters. +**
    • The database connection closes. SQLite does not make any guarantees +** about the order in which destructors are called, only that all +** destructors will be called exactly once at some point during the +** database connection closing process. +**
    +** +** SQLite does not do anything with client data other than invoke +** destructors on the client data at the appropriate time. The intended +** use for client data is to provide a mechanism for wrapper libraries +** to store additional information about an SQLite database connection. +** +** There is no limit (other than available memory) on the number of different +** client data pointers (with different names) that can be attached to a +** single database connection. However, the implementation is optimized +** for the case of having only one or two different client data names. +** Applications and wrapper libraries are discouraged from using more than +** one client data name each. +** +** There is no way to enumerate the client data pointers +** associated with a database connection. The N parameter can be thought +** of as a secret key such that only code that knows the secret key is able +** to access the associated data. +** +** Security Warning: These interfaces should not be exposed in scripting +** languages or in other circumstances where it might be possible for an +** an attacker to invoke them. Any agent that can invoke these interfaces +** can probably also take control of the process. +** +** Database connection client data is only available for SQLite +** version 3.44.0 ([dateof:3.44.0]) and later. +** +** See also: [sqlite3_set_auxdata()] and [sqlite3_get_auxdata()]. +*/ +SQLITE_API void *sqlite3_get_clientdata(sqlite3*,const char*); +SQLITE_API int sqlite3_set_clientdata(sqlite3*, const char*, void*, void(*)(void*)); /* ** CAPI3REF: Constants Defining Special Destructor Behavior @@ -6448,6 +6538,20 @@ SQLITE_API int sqlite3_result_zeroblob64(sqlite3_context*, sqlite3_uint64 n); ** higher order bits are discarded. ** The number of subtype bytes preserved by SQLite might increase ** in future releases of SQLite. +** +** Every [application-defined SQL function] that invokes this interface +** should include the [SQLITE_RESULT_SUBTYPE] property in its +** text encoding argument when the SQL function is +** [sqlite3_create_function|registered]. If the [SQLITE_RESULT_SUBTYPE] +** property is omitted from the function that invokes sqlite3_result_subtype(), +** then in some cases the sqlite3_result_subtype() might fail to set +** the result subtype. +** +** If SQLite is compiled with -DSQLITE_STRICT_SUBTYPE=1, then any +** SQL function that invokes the sqlite3_result_subtype() interface +** and that does not have the SQLITE_RESULT_SUBTYPE property will raise +** an error. Future versions of SQLite might enable -DSQLITE_STRICT_SUBTYPE=1 +** by default. */ SQLITE_API void sqlite3_result_subtype(sqlite3_context*,unsigned int); @@ -6879,7 +6983,7 @@ SQLITE_API int sqlite3_db_readonly(sqlite3 *db, const char *zDbName); SQLITE_API int sqlite3_txn_state(sqlite3*,const char *zSchema); /* -** CAPI3REF: Allowed return values from [sqlite3_txn_state()] +** CAPI3REF: Allowed return values from sqlite3_txn_state() ** KEYWORDS: {transaction state} ** ** These constants define the current transaction state of a database file. @@ -7011,7 +7115,7 @@ SQLITE_API void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*); ** ^Each call to the sqlite3_autovacuum_pages() interface overrides all ** previous invocations for that database connection. ^If the callback ** argument (C) to sqlite3_autovacuum_pages(D,C,P,X) is a NULL pointer, -** then the autovacuum steps callback is cancelled. The return value +** then the autovacuum steps callback is canceled. The return value ** from sqlite3_autovacuum_pages() is normally SQLITE_OK, but might ** be some other error code if something goes wrong. The current ** implementation will only return SQLITE_OK or SQLITE_MISUSE, but other @@ -7530,6 +7634,10 @@ struct sqlite3_module { /* The methods above are in versions 1 and 2 of the sqlite_module object. ** Those below are for version 3 and greater. */ int (*xShadowName)(const char*); + /* The methods above are in versions 1 through 3 of the sqlite_module object. + ** Those below are for version 4 and greater. */ + int (*xIntegrity)(sqlite3_vtab *pVTab, const char *zSchema, + const char *zTabName, int mFlags, char **pzErr); }; /* @@ -8017,7 +8125,7 @@ SQLITE_API int sqlite3_blob_reopen(sqlite3_blob *, sqlite3_int64); ** code is returned and the transaction rolled back. ** ** Calling this function with an argument that is not a NULL pointer or an -** open blob handle results in undefined behaviour. ^Calling this routine +** open blob handle results in undefined behavior. ^Calling this routine ** with a null pointer (such as would be returned by a failed call to ** [sqlite3_blob_open()]) is a harmless no-op. ^Otherwise, if this function ** is passed a valid open blob handle, the values returned by the @@ -8244,9 +8352,11 @@ SQLITE_API int sqlite3_vfs_unregister(sqlite3_vfs*); ** ** ^(Some systems (for example, Windows 95) do not support the operation ** implemented by sqlite3_mutex_try(). On those systems, sqlite3_mutex_try() -** will always return SQLITE_BUSY. The SQLite core only ever uses -** sqlite3_mutex_try() as an optimization so this is acceptable -** behavior.)^ +** will always return SQLITE_BUSY. In most cases the SQLite core only uses +** sqlite3_mutex_try() as an optimization, so this is acceptable +** behavior. The exceptions are unix builds that set the +** SQLITE_ENABLE_SETLK_TIMEOUT build option. In that case a working +** sqlite3_mutex_try() is required.)^ ** ** ^The sqlite3_mutex_leave() routine exits a mutex that was ** previously entered by the same thread. The behavior @@ -8497,6 +8607,7 @@ SQLITE_API int sqlite3_test_control(int op, ...); #define SQLITE_TESTCTRL_PRNG_SAVE 5 #define SQLITE_TESTCTRL_PRNG_RESTORE 6 #define SQLITE_TESTCTRL_PRNG_RESET 7 /* NOT USED */ +#define SQLITE_TESTCTRL_FK_NO_ACTION 7 #define SQLITE_TESTCTRL_BITVEC_TEST 8 #define SQLITE_TESTCTRL_FAULT_INSTALL 9 #define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10 @@ -8504,6 +8615,7 @@ SQLITE_API int sqlite3_test_control(int op, ...); #define SQLITE_TESTCTRL_ASSERT 12 #define SQLITE_TESTCTRL_ALWAYS 13 #define SQLITE_TESTCTRL_RESERVE 14 /* NOT USED */ +#define SQLITE_TESTCTRL_JSON_SELFCHECK 14 #define SQLITE_TESTCTRL_OPTIMIZATIONS 15 #define SQLITE_TESTCTRL_ISKEYWORD 16 /* NOT USED */ #define SQLITE_TESTCTRL_SCRATCHMALLOC 17 /* NOT USED */ @@ -9558,8 +9670,8 @@ SQLITE_API int sqlite3_backup_pagecount(sqlite3_backup *p); ** blocked connection already has a registered unlock-notify callback, ** then the new callback replaces the old.)^ ^If sqlite3_unlock_notify() is ** called with a NULL pointer as its second argument, then any existing -** unlock-notify callback is cancelled. ^The blocked connections -** unlock-notify callback may also be cancelled by closing the blocked +** unlock-notify callback is canceled. ^The blocked connections +** unlock-notify callback may also be canceled by closing the blocked ** connection using [sqlite3_close()]. ** ** The unlock-notify callback is not reentrant. If an application invokes @@ -10862,6 +10974,13 @@ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_recover(sqlite3 *db, const c ** SQLITE_SERIALIZE_NOCOPY bit is set but no contiguous copy ** of the database exists. ** +** After the call, if the SQLITE_SERIALIZE_NOCOPY bit had been set, +** the returned buffer content will remain accessible and unchanged +** until either the next write operation on the connection or when +** the connection is closed, and applications must not modify the +** buffer. If the bit had been clear, the returned buffer will not +** be accessed by SQLite after the call. +** ** A call to sqlite3_serialize(D,S,P,F) might return NULL even if the ** SQLITE_SERIALIZE_NOCOPY bit is omitted from argument F if a memory ** allocation error occurs. @@ -10910,6 +11029,9 @@ SQLITE_API unsigned char *sqlite3_serialize( ** SQLite will try to increase the buffer size using sqlite3_realloc64() ** if writes on the database cause it to grow larger than M bytes. ** +** Applications must not modify the buffer P or invalidate it before +** the database connection D is closed. +** ** The sqlite3_deserialize() interface will fail with SQLITE_BUSY if the ** database is currently in a read transaction or is involved in a backup ** operation. @@ -10918,6 +11040,13 @@ SQLITE_API unsigned char *sqlite3_serialize( ** S argument to sqlite3_deserialize(D,S,P,N,M,F) is "temp" then the ** function returns SQLITE_ERROR. ** +** The deserialized database should not be in [WAL mode]. If the database +** is in WAL mode, then any attempt to use the database file will result +** in an [SQLITE_CANTOPEN] error. The application can set the +** [file format version numbers] (bytes 18 and 19) of the input database P +** to 0x01 prior to invoking sqlite3_deserialize(D,S,P,N,M,F) to force the +** database file into rollback mode and work around this limitation. +** ** If sqlite3_deserialize(D,S,P,N,M,F) fails for any reason and if the ** SQLITE_DESERIALIZE_FREEONCLOSE bit is set in argument F, then ** [sqlite3_free()] is invoked on argument P prior to returning. @@ -11990,6 +12119,18 @@ SQLITE_API int sqlite3changeset_concat( ); +/* +** CAPI3REF: Upgrade the Schema of a Changeset/Patchset +*/ +SQLITE_API int sqlite3changeset_upgrade( + sqlite3 *db, + const char *zDb, + int nIn, const void *pIn, /* Input changeset */ + int *pnOut, void **ppOut /* OUT: Inverse of input */ +); + + + /* ** CAPI3REF: Changegroup Handle ** @@ -12036,6 +12177,38 @@ typedef struct sqlite3_changegroup sqlite3_changegroup; */ SQLITE_API int sqlite3changegroup_new(sqlite3_changegroup **pp); +/* +** CAPI3REF: Add a Schema to a Changegroup +** METHOD: sqlite3_changegroup_schema +** +** This method may be used to optionally enforce the rule that the changesets +** added to the changegroup handle must match the schema of database zDb +** ("main", "temp", or the name of an attached database). If +** sqlite3changegroup_add() is called to add a changeset that is not compatible +** with the configured schema, SQLITE_SCHEMA is returned and the changegroup +** object is left in an undefined state. +** +** A changeset schema is considered compatible with the database schema in +** the same way as for sqlite3changeset_apply(). Specifically, for each +** table in the changeset, there exists a database table with: +** +**
      +**
    • The name identified by the changeset, and +**
    • at least as many columns as recorded in the changeset, and +**
    • the primary key columns in the same position as recorded in +** the changeset. +**
    +** +** The output of the changegroup object always has the same schema as the +** database nominated using this function. In cases where changesets passed +** to sqlite3changegroup_add() have fewer columns than the corresponding table +** in the database schema, these are filled in using the default column +** values from the database schema. This makes it possible to combined +** changesets that have different numbers of columns for a single table +** within a changegroup, provided that they are otherwise compatible. +*/ +SQLITE_API int sqlite3changegroup_schema(sqlite3_changegroup*, sqlite3*, const char *zDb); + /* ** CAPI3REF: Add A Changeset To A Changegroup ** METHOD: sqlite3_changegroup @@ -12104,13 +12277,18 @@ SQLITE_API int sqlite3changegroup_new(sqlite3_changegroup **pp); ** If the new changeset contains changes to a table that is already present ** in the changegroup, then the number of columns and the position of the ** primary key columns for the table must be consistent. If this is not the -** case, this function fails with SQLITE_SCHEMA. If the input changeset -** appears to be corrupt and the corruption is detected, SQLITE_CORRUPT is -** returned. Or, if an out-of-memory condition occurs during processing, this -** function returns SQLITE_NOMEM. In all cases, if an error occurs the state -** of the final contents of the changegroup is undefined. +** case, this function fails with SQLITE_SCHEMA. Except, if the changegroup +** object has been configured with a database schema using the +** sqlite3changegroup_schema() API, then it is possible to combine changesets +** with different numbers of columns for a single table, provided that +** they are otherwise compatible. ** -** If no error occurs, SQLITE_OK is returned. +** If the input changeset appears to be corrupt and the corruption is +** detected, SQLITE_CORRUPT is returned. Or, if an out-of-memory condition +** occurs during processing, this function returns SQLITE_NOMEM. +** +** In all cases, if an error occurs the state of the final contents of the +** changegroup is undefined. If no error occurs, SQLITE_OK is returned. */ SQLITE_API int sqlite3changegroup_add(sqlite3_changegroup*, int nData, void *pData); @@ -12375,10 +12553,17 @@ SQLITE_API int sqlite3changeset_apply_v2( **
  1. an insert change if all fields of the conflicting row match ** the row being inserted. ** +** +**
    SQLITE_CHANGESETAPPLY_FKNOACTION
    +** If this flag it set, then all foreign key constraints in the target +** database behave as if they were declared with "ON UPDATE NO ACTION ON +** DELETE NO ACTION", even if they are actually CASCADE, RESTRICT, SET NULL +** or SET DEFAULT. */ #define SQLITE_CHANGESETAPPLY_NOSAVEPOINT 0x0001 #define SQLITE_CHANGESETAPPLY_INVERT 0x0002 #define SQLITE_CHANGESETAPPLY_IGNORENOOP 0x0004 +#define SQLITE_CHANGESETAPPLY_FKNOACTION 0x0008 /* ** CAPI3REF: Constants Passed To The Conflict Handler @@ -12944,8 +13129,11 @@ struct Fts5PhraseIter { ** created with the "columnsize=0" option. ** ** xColumnText: -** This function attempts to retrieve the text of column iCol of the -** current document. If successful, (*pz) is set to point to a buffer +** If parameter iCol is less than zero, or greater than or equal to the +** number of columns in the table, SQLITE_RANGE is returned. +** +** Otherwise, this function attempts to retrieve the text of column iCol of +** the current document. If successful, (*pz) is set to point to a buffer ** containing the text in utf-8 encoding, (*pn) is set to the size in bytes ** (not characters) of the buffer and SQLITE_OK is returned. Otherwise, ** if an error occurs, an SQLite error code is returned and the final values @@ -12955,8 +13143,10 @@ struct Fts5PhraseIter { ** Returns the number of phrases in the current query expression. ** ** xPhraseSize: -** Returns the number of tokens in phrase iPhrase of the query. Phrases -** are numbered starting from zero. +** If parameter iCol is less than zero, or greater than or equal to the +** number of phrases in the current query, as returned by xPhraseCount, +** 0 is returned. Otherwise, this function returns the number of tokens in +** phrase iPhrase of the query. Phrases are numbered starting from zero. ** ** xInstCount: ** Set *pnInst to the total number of occurrences of all phrases within @@ -12972,12 +13162,13 @@ struct Fts5PhraseIter { ** Query for the details of phrase match iIdx within the current row. ** Phrase matches are numbered starting from zero, so the iIdx argument ** should be greater than or equal to zero and smaller than the value -** output by xInstCount(). +** output by xInstCount(). If iIdx is less than zero or greater than +** or equal to the value returned by xInstCount(), SQLITE_RANGE is returned. ** -** Usually, output parameter *piPhrase is set to the phrase number, *piCol +** Otherwise, output parameter *piPhrase is set to the phrase number, *piCol ** to the column in which it occurs and *piOff the token offset of the -** first token of the phrase. Returns SQLITE_OK if successful, or an error -** code (i.e. SQLITE_NOMEM) if an error occurs. +** first token of the phrase. SQLITE_OK is returned if successful, or an +** error code (i.e. SQLITE_NOMEM) if an error occurs. ** ** This API can be quite slow if used with an FTS5 table created with the ** "detail=none" or "detail=column" option. @@ -13003,6 +13194,10 @@ struct Fts5PhraseIter { ** Invoking Api.xUserData() returns a copy of the pointer passed as ** the third argument to pUserData. ** +** If parameter iPhrase is less than zero, or greater than or equal to +** the number of phrases in the query, as returned by xPhraseCount(), +** this function returns SQLITE_RANGE. +** ** If the callback function returns any value other than SQLITE_OK, the ** query is abandoned and the xQueryPhrase function returns immediately. ** If the returned value is SQLITE_DONE, xQueryPhrase returns SQLITE_OK. @@ -13117,9 +13312,42 @@ struct Fts5PhraseIter { ** ** xPhraseNextColumn() ** See xPhraseFirstColumn above. +** +** xQueryToken(pFts5, iPhrase, iToken, ppToken, pnToken) +** This is used to access token iToken of phrase iPhrase of the current +** query. Before returning, output parameter *ppToken is set to point +** to a buffer containing the requested token, and *pnToken to the +** size of this buffer in bytes. +** +** If iPhrase or iToken are less than zero, or if iPhrase is greater than +** or equal to the number of phrases in the query as reported by +** xPhraseCount(), or if iToken is equal to or greater than the number of +** tokens in the phrase, SQLITE_RANGE is returned and *ppToken and *pnToken + are both zeroed. +** +** The output text is not a copy of the query text that specified the +** token. It is the output of the tokenizer module. For tokendata=1 +** tables, this includes any embedded 0x00 and trailing data. +** +** xInstToken(pFts5, iIdx, iToken, ppToken, pnToken) +** This is used to access token iToken of phrase hit iIdx within the +** current row. If iIdx is less than zero or greater than or equal to the +** value returned by xInstCount(), SQLITE_RANGE is returned. Otherwise, +** output variable (*ppToken) is set to point to a buffer containing the +** matching document token, and (*pnToken) to the size of that buffer in +** bytes. This API is not available if the specified token matches a +** prefix query term. In that case both output variables are always set +** to 0. +** +** The output text is not a copy of the document text that was tokenized. +** It is the output of the tokenizer module. For tokendata=1 tables, this +** includes any embedded 0x00 and trailing data. +** +** This API can be quite slow if used with an FTS5 table created with the +** "detail=none" or "detail=column" option. */ struct Fts5ExtensionApi { - int iVersion; /* Currently always set to 2 */ + int iVersion; /* Currently always set to 3 */ void *(*xUserData)(Fts5Context*); @@ -13154,6 +13382,13 @@ struct Fts5ExtensionApi { int (*xPhraseFirstColumn)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*); void (*xPhraseNextColumn)(Fts5Context*, Fts5PhraseIter*, int *piCol); + + /* Below this point are iVersion>=3 only */ + int (*xQueryToken)(Fts5Context*, + int iPhrase, int iToken, + const char **ppToken, int *pnToken + ); + int (*xInstToken)(Fts5Context*, int iIdx, int iToken, const char**, int*); }; /* @@ -13640,7 +13875,7 @@ struct fts5_api { ** max_page_count macro. */ #ifndef SQLITE_MAX_PAGE_COUNT -# define SQLITE_MAX_PAGE_COUNT 1073741823 +# define SQLITE_MAX_PAGE_COUNT 0xfffffffe /* 4294967294 */ #endif /* @@ -13769,6 +14004,29 @@ struct fts5_api { # endif #endif +/* +** Enable SQLITE_USE_SEH by default on MSVC builds. Only omit +** SEH support if the -DSQLITE_OMIT_SEH option is given. +*/ +#if defined(_MSC_VER) && !defined(SQLITE_OMIT_SEH) +# define SQLITE_USE_SEH 1 +#else +# undef SQLITE_USE_SEH +#endif + +/* +** Enable SQLITE_DIRECT_OVERFLOW_READ, unless the build explicitly +** disables it using -DSQLITE_DIRECT_OVERFLOW_READ=0 +*/ +#if defined(SQLITE_DIRECT_OVERFLOW_READ) && SQLITE_DIRECT_OVERFLOW_READ+1==1 + /* Disable if -DSQLITE_DIRECT_OVERFLOW_READ=0 */ +# undef SQLITE_DIRECT_OVERFLOW_READ +#else + /* In all other cases, enable */ +# define SQLITE_DIRECT_OVERFLOW_READ 1 +#endif + + /* ** The SQLITE_THREADSAFE macro must be defined as 0, 1, or 2. ** 0 means mutexes are permanently disable and the library is never @@ -14662,16 +14920,33 @@ typedef INT16_TYPE LogEst; ** using C-preprocessor macros. If that is unsuccessful, or if ** -DSQLITE_BYTEORDER=0 is set, then byte-order is determined ** at run-time. +** +** If you are building SQLite on some obscure platform for which the +** following ifdef magic does not work, you can always include either: +** +** -DSQLITE_BYTEORDER=1234 +** +** or +** +** -DSQLITE_BYTEORDER=4321 +** +** to cause the build to work for little-endian or big-endian processors, +** respectively. */ -#ifndef SQLITE_BYTEORDER -# if defined(i386) || defined(__i386__) || defined(_M_IX86) || \ +#ifndef SQLITE_BYTEORDER /* Replicate changes at tag-20230904a */ +# if defined(__BYTE_ORDER__) && __BYTE_ORDER__==__ORDER_BIG_ENDIAN__ +# define SQLITE_BYTEORDER 4321 +# elif defined(__BYTE_ORDER__) && __BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__ +# define SQLITE_BYTEORDER 1234 +# elif defined(__BIG_ENDIAN__) && __BIG_ENDIAN__==1 +# define SQLITE_BYTEORDER 4321 +# elif defined(i386) || defined(__i386__) || defined(_M_IX86) || \ defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || \ defined(_M_AMD64) || defined(_M_ARM) || defined(__x86) || \ defined(__ARMEL__) || defined(__AARCH64EL__) || defined(_M_ARM64) -# define SQLITE_BYTEORDER 1234 -# elif defined(sparc) || defined(__ppc__) || \ - defined(__ARMEB__) || defined(__AARCH64EB__) -# define SQLITE_BYTEORDER 4321 +# define SQLITE_BYTEORDER 1234 +# elif defined(sparc) || defined(__ARMEB__) || defined(__AARCH64EB__) +# define SQLITE_BYTEORDER 4321 # else # define SQLITE_BYTEORDER 0 # endif @@ -14995,6 +15270,7 @@ typedef struct Column Column; typedef struct Cte Cte; typedef struct CteUse CteUse; typedef struct Db Db; +typedef struct DbClientData DbClientData; typedef struct DbFixer DbFixer; typedef struct Schema Schema; typedef struct Expr Expr; @@ -15633,7 +15909,7 @@ SQLITE_PRIVATE sqlite3_file *sqlite3PagerJrnlFile(Pager*); SQLITE_PRIVATE const char *sqlite3PagerJournalname(Pager*); SQLITE_PRIVATE void *sqlite3PagerTempSpace(Pager*); SQLITE_PRIVATE int sqlite3PagerIsMemdb(Pager*); -SQLITE_PRIVATE void sqlite3PagerCacheStat(Pager *, int, int, int *); +SQLITE_PRIVATE void sqlite3PagerCacheStat(Pager *, int, int, u64*); SQLITE_PRIVATE void sqlite3PagerClearCache(Pager*); SQLITE_PRIVATE int sqlite3SectorSize(sqlite3_file *); @@ -16220,6 +16496,7 @@ typedef struct VdbeOpList VdbeOpList; #define P4_INT64 (-13) /* P4 is a 64-bit signed integer */ #define P4_INTARRAY (-14) /* P4 is a vector of 32-bit integers */ #define P4_FUNCCTX (-15) /* P4 is a pointer to an sqlite3_context object */ +#define P4_TABLEREF (-16) /* Like P4_TABLE, but reference counted */ /* Error message codes for OP_Halt */ #define P5_ConstraintNotNull 1 @@ -16435,19 +16712,22 @@ typedef struct VdbeOpList VdbeOpList; #define OP_VCreate 171 #define OP_VDestroy 172 #define OP_VOpen 173 -#define OP_VInitIn 174 /* synopsis: r[P2]=ValueList(P1,P3) */ -#define OP_VColumn 175 /* synopsis: r[P3]=vcolumn(P2) */ -#define OP_VRename 176 -#define OP_Pagecount 177 -#define OP_MaxPgcnt 178 -#define OP_ClrSubtype 179 /* synopsis: r[P1].subtype = 0 */ -#define OP_FilterAdd 180 /* synopsis: filter(P1) += key(P3@P4) */ -#define OP_Trace 181 -#define OP_CursorHint 182 -#define OP_ReleaseReg 183 /* synopsis: release r[P1@P2] mask P3 */ -#define OP_Noop 184 -#define OP_Explain 185 -#define OP_Abortable 186 +#define OP_VCheck 174 +#define OP_VInitIn 175 /* synopsis: r[P2]=ValueList(P1,P3) */ +#define OP_VColumn 176 /* synopsis: r[P3]=vcolumn(P2) */ +#define OP_VRename 177 +#define OP_Pagecount 178 +#define OP_MaxPgcnt 179 +#define OP_ClrSubtype 180 /* synopsis: r[P1].subtype = 0 */ +#define OP_GetSubtype 181 /* synopsis: r[P2] = r[P1].subtype */ +#define OP_SetSubtype 182 /* synopsis: r[P2].subtype = r[P1] */ +#define OP_FilterAdd 183 /* synopsis: filter(P1) += key(P3@P4) */ +#define OP_Trace 184 +#define OP_CursorHint 185 +#define OP_ReleaseReg 186 /* synopsis: release r[P1@P2] mask P3 */ +#define OP_Noop 187 +#define OP_Explain 188 +#define OP_Abortable 189 /* Properties such as "out2" or "jump" that are specified in ** comments following the "case" for each opcode in the vdbe.c @@ -16482,9 +16762,9 @@ typedef struct VdbeOpList VdbeOpList; /* 144 */ 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,\ /* 152 */ 0x00, 0x10, 0x00, 0x00, 0x06, 0x10, 0x00, 0x04,\ /* 160 */ 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ -/* 168 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x50, 0x40,\ -/* 176 */ 0x00, 0x10, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00,\ -/* 184 */ 0x00, 0x00, 0x00,} +/* 168 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x10, 0x50,\ +/* 176 */ 0x40, 0x00, 0x10, 0x10, 0x02, 0x12, 0x12, 0x00,\ +/* 184 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,} /* The resolve3P2Values() routine is able to run faster if it knows ** the value of the largest JUMP opcode. The smaller the maximum @@ -17393,6 +17673,7 @@ struct sqlite3 { i64 nDeferredCons; /* Net deferred constraints this transaction. */ i64 nDeferredImmCons; /* Net deferred immediate constraints */ int *pnBytesFreed; /* If not NULL, increment this in DbFree() */ + DbClientData *pDbData; /* sqlite3_set_clientdata() content */ #ifdef SQLITE_ENABLE_UNLOCK_NOTIFY /* The following variables are all protected by the STATIC_MAIN ** mutex, not by sqlite3.mutex. They are used by code in notify.c. @@ -17475,6 +17756,7 @@ struct sqlite3 { /* the count using a callback. */ #define SQLITE_CorruptRdOnly HI(0x00002) /* Prohibit writes due to error */ #define SQLITE_ReadUncommit HI(0x00004) /* READ UNCOMMITTED in shared-cache */ +#define SQLITE_FkNoAction HI(0x00008) /* Treat all FK as NO ACTION */ /* Flags used only if debugging */ #ifdef SQLITE_DEBUG @@ -17643,14 +17925,15 @@ struct FuncDestructor { #define SQLITE_FUNC_SLOCHNG 0x2000 /* "Slow Change". Value constant during a ** single query - might change over time */ #define SQLITE_FUNC_TEST 0x4000 /* Built-in testing functions */ -/* 0x8000 -- available for reuse */ +#define SQLITE_FUNC_RUNONLY 0x8000 /* Cannot be used by valueFromFunction */ #define SQLITE_FUNC_WINDOW 0x00010000 /* Built-in window-only function */ #define SQLITE_FUNC_INTERNAL 0x00040000 /* For use by NestedParse() only */ #define SQLITE_FUNC_DIRECT 0x00080000 /* Not for use in TRIGGERs or VIEWs */ -#define SQLITE_FUNC_SUBTYPE 0x00100000 /* Result likely to have sub-type */ +/* SQLITE_SUBTYPE 0x00100000 // Consumer of subtypes */ #define SQLITE_FUNC_UNSAFE 0x00200000 /* Function has side effects */ #define SQLITE_FUNC_INLINE 0x00400000 /* Functions implemented in-line */ #define SQLITE_FUNC_BUILTIN 0x00800000 /* This is a built-in function */ +/* SQLITE_RESULT_SUBTYPE 0x01000000 // Generator of subtypes */ #define SQLITE_FUNC_ANYORDER 0x08000000 /* count/min/max aggregate */ /* Identifier numbers for each in-line function */ @@ -17742,10 +18025,11 @@ struct FuncDestructor { #define MFUNCTION(zName, nArg, xPtr, xFunc) \ {nArg, SQLITE_FUNC_BUILTIN|SQLITE_FUNC_CONSTANT|SQLITE_UTF8, \ xPtr, 0, xFunc, 0, 0, 0, #zName, {0} } -#define JFUNCTION(zName, nArg, iArg, xFunc) \ - {nArg, SQLITE_FUNC_BUILTIN|SQLITE_DETERMINISTIC|\ - SQLITE_FUNC_CONSTANT|SQLITE_UTF8, \ - SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, 0, #zName, {0} } +#define JFUNCTION(zName, nArg, bUseCache, bWS, bRS, bJsonB, iArg, xFunc) \ + {nArg, SQLITE_FUNC_BUILTIN|SQLITE_DETERMINISTIC|SQLITE_FUNC_CONSTANT|\ + SQLITE_UTF8|((bUseCache)*SQLITE_FUNC_RUNONLY)|\ + ((bRS)*SQLITE_SUBTYPE)|((bWS)*SQLITE_RESULT_SUBTYPE), \ + SQLITE_INT_TO_PTR(iArg|((bJsonB)*JSON_BLOB)),0,xFunc,0, 0, 0, #zName, {0} } #define INLINE_FUNC(zName, nArg, iArg, mFlags) \ {nArg, SQLITE_FUNC_BUILTIN|\ SQLITE_UTF8|SQLITE_FUNC_INLINE|SQLITE_FUNC_CONSTANT|(mFlags), \ @@ -18380,6 +18664,7 @@ struct Index { unsigned isCovering:1; /* True if this is a covering index */ unsigned noSkipScan:1; /* Do not try to use skip-scan if true */ unsigned hasStat1:1; /* aiRowLogEst values come from sqlite_stat1 */ + unsigned bLowQual:1; /* sqlite_stat1 says this is a low-quality index */ unsigned bNoQuery:1; /* Do not use this index to optimize queries */ unsigned bAscKeyBug:1; /* True if the bba7b69f9849b5bf bug applies */ unsigned bHasVCol:1; /* Index references one or more VIRTUAL columns */ @@ -18490,6 +18775,10 @@ struct AggInfo { FuncDef *pFunc; /* The aggregate function implementation */ int iDistinct; /* Ephemeral table used to enforce DISTINCT */ int iDistAddr; /* Address of OP_OpenEphemeral */ + int iOBTab; /* Ephemeral table to implement ORDER BY */ + u8 bOBPayload; /* iOBTab has payload columns separate from key */ + u8 bOBUnique; /* Enforce uniqueness on iOBTab keys */ + u8 bUseSubtype; /* Transfer subtype info through sorter */ } *aFunc; int nFunc; /* Number of entries in aFunc[] */ u32 selId; /* Select to which this AggInfo belongs */ @@ -18674,7 +18963,7 @@ struct Expr { #define EP_Reduced 0x004000 /* Expr struct EXPR_REDUCEDSIZE bytes only */ #define EP_Win 0x008000 /* Contains window functions */ #define EP_TokenOnly 0x010000 /* Expr struct EXPR_TOKENONLYSIZE bytes only */ - /* 0x020000 // Available for reuse */ +#define EP_FullSize 0x020000 /* Expr structure must remain full sized */ #define EP_IfNullRow 0x040000 /* The TK_IF_NULL_ROW opcode */ #define EP_Unlikely 0x080000 /* unlikely() or likelihood() function */ #define EP_ConstFunc 0x100000 /* A SQLITE_FUNC_CONSTANT or _SLOCHNG function */ @@ -18704,6 +18993,7 @@ struct Expr { #define ExprClearProperty(E,P) (E)->flags&=~(P) #define ExprAlwaysTrue(E) (((E)->flags&(EP_OuterON|EP_IsTrue))==EP_IsTrue) #define ExprAlwaysFalse(E) (((E)->flags&(EP_OuterON|EP_IsFalse))==EP_IsFalse) +#define ExprIsFullSize(E) (((E)->flags&(EP_Reduced|EP_TokenOnly))==0) /* Macros used to ensure that the correct members of unions are accessed ** in Expr. @@ -18821,6 +19111,7 @@ struct ExprList { #define ENAME_NAME 0 /* The AS clause of a result set */ #define ENAME_SPAN 1 /* Complete text of the result set expression */ #define ENAME_TAB 2 /* "DB.TABLE.NAME" for the result set */ +#define ENAME_ROWID 3 /* "DB.TABLE._rowid_" for * expansion of rowid */ /* ** An instance of this structure can hold a simple list of identifiers, @@ -19021,6 +19312,7 @@ struct NameContext { int nRef; /* Number of names resolved by this context */ int nNcErr; /* Number of errors encountered while resolving names */ int ncFlags; /* Zero or more NC_* flags defined below */ + u32 nNestedSelect; /* Number of nested selects using this NC */ Select *pWinSelect; /* SELECT statement for any window functions */ }; @@ -19429,6 +19721,7 @@ struct Parse { int *aLabel; /* Space to hold the labels */ ExprList *pConstExpr;/* Constant expressions */ IndexedExpr *pIdxEpr;/* List of expressions used by active indexes */ + IndexedExpr *pIdxPartExpr; /* Exprs constrained by index WHERE clauses */ Token constraintName;/* Name of the constraint currently being parsed */ yDbMask writeMask; /* Start a write transaction on these databases */ yDbMask cookieMask; /* Bitmask of schema verified databases */ @@ -19700,6 +19993,7 @@ struct Returning { int iRetCur; /* Transient table holding RETURNING results */ int nRetCol; /* Number of in pReturnEL after expansion */ int iRetReg; /* Register array for holding a row of RETURNING */ + char zName[40]; /* Name of trigger: "sqlite_returning_%p" */ }; /* @@ -19735,6 +20029,9 @@ struct sqlite3_str { ** ** 3. Make a (read-only) copy of a read-only RCStr string using ** sqlite3RCStrRef(). +** +** "String" is in the name, but an RCStr object can also be used to hold +** binary data. */ struct RCStr { u64 nRCRef; /* Number of references */ @@ -19793,6 +20090,9 @@ struct Sqlite3Config { u8 bSmallMalloc; /* Avoid large memory allocations if true */ u8 bExtraSchemaChecks; /* Verify type,name,tbl_name in schema */ u8 bUseLongDouble; /* Make use of long double */ +#ifdef SQLITE_DEBUG + u8 bJsonSelfcheck; /* Double-check JSON parsing */ +#endif int mxStrlen; /* Maximum string length */ int neverCorrupt; /* Database is always well-formed */ int szLookaside; /* Default lookaside buffer size */ @@ -20000,6 +20300,16 @@ struct CteUse { }; +/* Client data associated with sqlite3_set_clientdata() and +** sqlite3_get_clientdata(). +*/ +struct DbClientData { + DbClientData *pNext; /* Next in a linked list */ + void *pData; /* The data */ + void (*xDestructor)(void*); /* Destructor. Might be NULL */ + char zName[1]; /* Name of this client data. MUST BE LAST */ +}; + #ifdef SQLITE_DEBUG /* ** An instance of the TreeView object is used for printing the content of @@ -20404,9 +20714,12 @@ SQLITE_PRIVATE void sqlite3PExprAddSelect(Parse*, Expr*, Select*); SQLITE_PRIVATE Expr *sqlite3ExprAnd(Parse*,Expr*, Expr*); SQLITE_PRIVATE Expr *sqlite3ExprSimplifiedAndOr(Expr*); SQLITE_PRIVATE Expr *sqlite3ExprFunction(Parse*,ExprList*, const Token*, int); +SQLITE_PRIVATE void sqlite3ExprAddFunctionOrderBy(Parse*,Expr*,ExprList*); +SQLITE_PRIVATE void sqlite3ExprOrderByAggregateError(Parse*,Expr*); SQLITE_PRIVATE void sqlite3ExprFunctionUsable(Parse*,const Expr*,const FuncDef*); SQLITE_PRIVATE void sqlite3ExprAssignVarNumber(Parse*, Expr*, u32); SQLITE_PRIVATE void sqlite3ExprDelete(sqlite3*, Expr*); +SQLITE_PRIVATE void sqlite3ExprDeleteGeneric(sqlite3*,void*); SQLITE_PRIVATE void sqlite3ExprDeferredDelete(Parse*, Expr*); SQLITE_PRIVATE void sqlite3ExprUnmapAndDelete(Parse*, Expr*); SQLITE_PRIVATE ExprList *sqlite3ExprListAppend(Parse*,ExprList*,Expr*); @@ -20416,6 +20729,7 @@ SQLITE_PRIVATE void sqlite3ExprListSetSortOrder(ExprList*,int,int); SQLITE_PRIVATE void sqlite3ExprListSetName(Parse*,ExprList*,const Token*,int); SQLITE_PRIVATE void sqlite3ExprListSetSpan(Parse*,ExprList*,const char*,const char*); SQLITE_PRIVATE void sqlite3ExprListDelete(sqlite3*, ExprList*); +SQLITE_PRIVATE void sqlite3ExprListDeleteGeneric(sqlite3*,void*); SQLITE_PRIVATE u32 sqlite3ExprListFlags(const ExprList*); SQLITE_PRIVATE int sqlite3IndexHasDuplicateRootPage(Index*); SQLITE_PRIVATE int sqlite3Init(sqlite3*, char**); @@ -20506,6 +20820,7 @@ SQLITE_PRIVATE int sqlite3DbMaskAllZero(yDbMask); SQLITE_PRIVATE void sqlite3DropTable(Parse*, SrcList*, int, int); SQLITE_PRIVATE void sqlite3CodeDropTable(Parse*, Table*, int, int); SQLITE_PRIVATE void sqlite3DeleteTable(sqlite3*, Table*); +SQLITE_PRIVATE void sqlite3DeleteTableGeneric(sqlite3*, void*); SQLITE_PRIVATE void sqlite3FreeIndex(sqlite3*, Index*); #ifndef SQLITE_OMIT_AUTOINCREMENT SQLITE_PRIVATE void sqlite3AutoincrementBegin(Parse *pParse); @@ -20542,6 +20857,7 @@ SQLITE_PRIVATE int sqlite3Select(Parse*, Select*, SelectDest*); SQLITE_PRIVATE Select *sqlite3SelectNew(Parse*,ExprList*,SrcList*,Expr*,ExprList*, Expr*,ExprList*,u32,Expr*); SQLITE_PRIVATE void sqlite3SelectDelete(sqlite3*, Select*); +SQLITE_PRIVATE void sqlite3SelectDeleteGeneric(sqlite3*,void*); SQLITE_PRIVATE Table *sqlite3SrcListLookup(Parse*, SrcList*); SQLITE_PRIVATE int sqlite3IsReadOnly(Parse*, Table*, Trigger*); SQLITE_PRIVATE void sqlite3OpenTable(Parse*, int iCur, int iDb, Table*, int); @@ -20640,6 +20956,7 @@ SQLITE_PRIVATE int sqlite3ExprIsInteger(const Expr*, int*); SQLITE_PRIVATE int sqlite3ExprCanBeNull(const Expr*); SQLITE_PRIVATE int sqlite3ExprNeedsNoAffinityChange(const Expr*, char); SQLITE_PRIVATE int sqlite3IsRowid(const char*); +SQLITE_PRIVATE const char *sqlite3RowidAlias(Table *pTab); SQLITE_PRIVATE void sqlite3GenerateRowDelete( Parse*,Table*,Trigger*,int,int,int,i16,u8,u8,u8,int); SQLITE_PRIVATE void sqlite3GenerateRowIndexDelete(Parse*, Table*, int, int, int*, int); @@ -20767,6 +21084,7 @@ SQLITE_PRIVATE int sqlite3Utf16ByteLen(const void *pData, int nChar); #endif SQLITE_PRIVATE int sqlite3Utf8CharLen(const char *pData, int nByte); SQLITE_PRIVATE u32 sqlite3Utf8Read(const u8**); +SQLITE_PRIVATE int sqlite3Utf8ReadLimited(const u8*, int, u32*); SQLITE_PRIVATE LogEst sqlite3LogEst(u64); SQLITE_PRIVATE LogEst sqlite3LogEstAdd(LogEst,LogEst); SQLITE_PRIVATE LogEst sqlite3LogEstFromDouble(double); @@ -20911,7 +21229,8 @@ SQLITE_PRIVATE int sqlite3MatchEName( const struct ExprList_item*, const char*, const char*, - const char* + const char*, + int* ); SQLITE_PRIVATE Bitmask sqlite3ExprColUsed(Expr*); SQLITE_PRIVATE u8 sqlite3StrIHash(const char*); @@ -20968,7 +21287,7 @@ SQLITE_PRIVATE int sqlite3ApiExit(sqlite3 *db, int); SQLITE_PRIVATE int sqlite3OpenTempDatabase(Parse *); SQLITE_PRIVATE char *sqlite3RCStrRef(char*); -SQLITE_PRIVATE void sqlite3RCStrUnref(char*); +SQLITE_PRIVATE void sqlite3RCStrUnref(void*); SQLITE_PRIVATE char *sqlite3RCStrNew(u64); SQLITE_PRIVATE char *sqlite3RCStrResize(char*,u64); @@ -21112,6 +21431,7 @@ SQLITE_PRIVATE Cte *sqlite3CteNew(Parse*,Token*,ExprList*,Select*,u8); SQLITE_PRIVATE void sqlite3CteDelete(sqlite3*,Cte*); SQLITE_PRIVATE With *sqlite3WithAdd(Parse*,With*,Cte*); SQLITE_PRIVATE void sqlite3WithDelete(sqlite3*,With*); +SQLITE_PRIVATE void sqlite3WithDeleteGeneric(sqlite3*,void*); SQLITE_PRIVATE With *sqlite3WithPush(Parse*, With*, u8); #else # define sqlite3CteNew(P,T,E,S) ((void*)0) @@ -21804,6 +22124,9 @@ static const char * const sqlite3azCompileOpt[] = { #ifdef SQLITE_EXPLAIN_ESTIMATED_ROWS "EXPLAIN_ESTIMATED_ROWS", #endif +#ifdef SQLITE_EXTRA_AUTOEXT + "EXTRA_AUTOEXT=" CTIMEOPT_VAL(SQLITE_EXTRA_AUTOEXT), +#endif #ifdef SQLITE_EXTRA_IFNULLROW "EXTRA_IFNULLROW", #endif @@ -22085,6 +22408,9 @@ static const char * const sqlite3azCompileOpt[] = { #ifdef SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS "OMIT_SCHEMA_VERSION_PRAGMAS", #endif +#ifdef SQLITE_OMIT_SEH + "OMIT_SEH", +#endif #ifdef SQLITE_OMIT_SHARED_CACHE "OMIT_SHARED_CACHE", #endif @@ -22483,6 +22809,9 @@ SQLITE_PRIVATE SQLITE_WSD struct Sqlite3Config sqlite3Config = { 0, /* bSmallMalloc */ 1, /* bExtraSchemaChecks */ sizeof(LONGDOUBLE_TYPE)>8, /* bUseLongDouble */ +#ifdef SQLITE_DEBUG + 0, /* bJsonSelfcheck */ +#endif 0x7ffffffe, /* mxStrlen */ 0, /* neverCorrupt */ SQLITE_DEFAULT_LOOKASIDE, /* szLookaside, nLookaside */ @@ -23735,7 +24064,7 @@ SQLITE_API int sqlite3_db_status( case SQLITE_DBSTATUS_CACHE_MISS: case SQLITE_DBSTATUS_CACHE_WRITE:{ int i; - int nRet = 0; + u64 nRet = 0; assert( SQLITE_DBSTATUS_CACHE_MISS==SQLITE_DBSTATUS_CACHE_HIT+1 ); assert( SQLITE_DBSTATUS_CACHE_WRITE==SQLITE_DBSTATUS_CACHE_HIT+2 ); @@ -23748,7 +24077,7 @@ SQLITE_API int sqlite3_db_status( *pHighwater = 0; /* IMP: R-42420-56072 */ /* IMP: R-54100-20147 */ /* IMP: R-29431-39229 */ - *pCurrent = nRet; + *pCurrent = (int)nRet & 0x7fffffff; break; } @@ -24817,6 +25146,12 @@ static int isDate( } computeJD(p); if( p->isError || !validJulianDay(p->iJD) ) return 1; + if( argc==1 && p->validYMD && p->D>28 ){ + /* Make sure a YYYY-MM-DD is normalized. + ** Example: 2023-02-31 -> 2023-03-03 */ + assert( p->validJD ); + p->validYMD = 0; + } return 0; } @@ -25044,13 +25379,16 @@ static void strftimeFunc( computeJD(&x); computeYMD_HMS(&x); for(i=j=0; zFmt[i]; i++){ + char cf; if( zFmt[i]!='%' ) continue; if( j12 ) h -= 12; + if( h==0 ) h = 12; + sqlite3_str_appendf(&sRes, cf=='I' ? "%02d" : "%2d", h); break; } case 'W': /* Fall thru */ @@ -25072,7 +25423,7 @@ static void strftimeFunc( y.D = 1; computeJD(&y); nDay = (int)((x.iJD-y.iJD+43200000)/86400000); - if( zFmt[i]=='W' ){ + if( cf=='W' ){ int wd; /* 0=Monday, 1=Tuesday, ... 6=Sunday */ wd = (int)(((x.iJD+43200000)/86400000)%7); sqlite3_str_appendf(&sRes,"%02d",(nDay+7-wd)/7); @@ -25093,6 +25444,19 @@ static void strftimeFunc( sqlite3_str_appendf(&sRes,"%02d",x.m); break; } + case 'p': /* Fall thru */ + case 'P': { + if( x.h>=12 ){ + sqlite3_str_append(&sRes, cf=='p' ? "PM" : "pm", 2); + }else{ + sqlite3_str_append(&sRes, cf=='p' ? "AM" : "am", 2); + } + break; + } + case 'R': { + sqlite3_str_appendf(&sRes, "%02d:%02d", x.h, x.m); + break; + } case 's': { if( x.useSubsec ){ sqlite3_str_appendf(&sRes,"%.3f", @@ -25107,9 +25471,15 @@ static void strftimeFunc( sqlite3_str_appendf(&sRes,"%02d",(int)x.s); break; } + case 'T': { + sqlite3_str_appendf(&sRes,"%02d:%02d:%02d", x.h, x.m, (int)x.s); + break; + } + case 'u': /* Fall thru */ case 'w': { - sqlite3_str_appendchar(&sRes, 1, - (char)(((x.iJD+129600000)/86400000) % 7) + '0'); + char c = (char)(((x.iJD+129600000)/86400000) % 7) + '0'; + if( c=='0' && cf=='u' ) c = '7'; + sqlite3_str_appendchar(&sRes, 1, c); break; } case 'Y': { @@ -28198,7 +28568,7 @@ static void checkMutexFree(sqlite3_mutex *p){ assert( SQLITE_MUTEX_FAST<2 ); assert( SQLITE_MUTEX_WARNONCONTENTION<2 ); -#if SQLITE_ENABLE_API_ARMOR +#ifdef SQLITE_ENABLE_API_ARMOR if( ((CheckMutex*)p)->iType<2 ) #endif { @@ -28870,7 +29240,7 @@ static sqlite3_mutex *pthreadMutexAlloc(int iType){ */ static void pthreadMutexFree(sqlite3_mutex *p){ assert( p->nRef==0 ); -#if SQLITE_ENABLE_API_ARMOR +#ifdef SQLITE_ENABLE_API_ARMOR if( p->id==SQLITE_MUTEX_FAST || p->id==SQLITE_MUTEX_RECURSIVE ) #endif { @@ -29223,7 +29593,7 @@ SQLITE_PRIVATE void sqlite3MemoryBarrier(void){ SQLITE_MEMORY_BARRIER; #elif defined(__GNUC__) __sync_synchronize(); -#elif MSVC_VERSION>=1300 +#elif MSVC_VERSION>=1400 _ReadWriteBarrier(); #elif defined(MemoryBarrier) MemoryBarrier(); @@ -30434,7 +30804,7 @@ SQLITE_PRIVATE int sqlite3ApiExit(sqlite3* db, int rc){ if( db->mallocFailed || rc ){ return apiHandleError(db, rc); } - return rc & db->errMask; + return 0; } /************** End of malloc.c **********************************************/ @@ -31810,7 +32180,7 @@ SQLITE_API void sqlite3_str_appendf(StrAccum *p, const char *zFormat, ...){ /***************************************************************************** -** Reference counted string storage +** Reference counted string/blob storage *****************************************************************************/ /* @@ -31830,7 +32200,7 @@ SQLITE_PRIVATE char *sqlite3RCStrRef(char *z){ ** Decrease the reference count by one. Free the string when the ** reference count reaches zero. */ -SQLITE_PRIVATE void sqlite3RCStrUnref(char *z){ +SQLITE_PRIVATE void sqlite3RCStrUnref(void *z){ RCStr *p = (RCStr*)z; assert( p!=0 ); p--; @@ -32293,6 +32663,7 @@ SQLITE_PRIVATE void sqlite3TreeViewWindow(TreeView *pView, const Window *pWin, u sqlite3TreeViewItem(pView, "FILTER", 1); sqlite3TreeViewExpr(pView, pWin->pFilter, 0); sqlite3TreeViewPop(&pView); + if( pWin->eFrmType==TK_FILTER ) return; } sqlite3TreeViewPush(&pView, more); if( pWin->zName ){ @@ -32302,7 +32673,7 @@ SQLITE_PRIVATE void sqlite3TreeViewWindow(TreeView *pView, const Window *pWin, u } if( pWin->zBase ) nElement++; if( pWin->pOrderBy ) nElement++; - if( pWin->eFrmType ) nElement++; + if( pWin->eFrmType!=0 && pWin->eFrmType!=TK_FILTER ) nElement++; if( pWin->eExclude ) nElement++; if( pWin->zBase ){ sqlite3TreeViewPush(&pView, (--nElement)>0); @@ -32315,7 +32686,7 @@ SQLITE_PRIVATE void sqlite3TreeViewWindow(TreeView *pView, const Window *pWin, u if( pWin->pOrderBy ){ sqlite3TreeViewExprList(pView, pWin->pOrderBy, (--nElement)>0, "ORDER-BY"); } - if( pWin->eFrmType ){ + if( pWin->eFrmType!=0 && pWin->eFrmType!=TK_FILTER ){ char zBuf[30]; const char *zFrmType = "ROWS"; if( pWin->eFrmType==TK_RANGE ) zFrmType = "RANGE"; @@ -32563,7 +32934,7 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m assert( ExprUseXList(pExpr) ); pFarg = pExpr->x.pList; #ifndef SQLITE_OMIT_WINDOWFUNC - pWin = ExprHasProperty(pExpr, EP_WinFunc) ? pExpr->y.pWin : 0; + pWin = IsWindowFunc(pExpr) ? pExpr->y.pWin : 0; #else pWin = 0; #endif @@ -32589,7 +32960,13 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m sqlite3TreeViewLine(pView, "FUNCTION %Q%s", pExpr->u.zToken, zFlgs); } if( pFarg ){ - sqlite3TreeViewExprList(pView, pFarg, pWin!=0, 0); + sqlite3TreeViewExprList(pView, pFarg, pWin!=0 || pExpr->pLeft, 0); + if( pExpr->pLeft ){ + Expr *pOB = pExpr->pLeft; + assert( pOB->op==TK_ORDER ); + assert( ExprUseXList(pOB) ); + sqlite3TreeViewExprList(pView, pOB->x.pList, pWin!=0, "ORDERBY"); + } } #ifndef SQLITE_OMIT_WINDOWFUNC if( pWin ){ @@ -32598,6 +32975,10 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m #endif break; } + case TK_ORDER: { + sqlite3TreeViewExprList(pView, pExpr->x.pList, 0, "ORDERBY"); + break; + } #ifndef SQLITE_OMIT_SUBQUERY case TK_EXISTS: { assert( ExprUseXSelect(pExpr) ); @@ -32651,7 +33032,7 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m assert( pExpr->x.pList->nExpr==2 ); pY = pExpr->x.pList->a[0].pExpr; pZ = pExpr->x.pList->a[1].pExpr; - sqlite3TreeViewLine(pView, "BETWEEN"); + sqlite3TreeViewLine(pView, "BETWEEN%s", zFlgs); sqlite3TreeViewExpr(pView, pX, 1); sqlite3TreeViewExpr(pView, pY, 1); sqlite3TreeViewExpr(pView, pZ, 0); @@ -33786,7 +34167,38 @@ SQLITE_PRIVATE u32 sqlite3Utf8Read( return c; } - +/* +** Read a single UTF8 character out of buffer z[], but reading no +** more than n characters from the buffer. z[] is not zero-terminated. +** +** Return the number of bytes used to construct the character. +** +** Invalid UTF8 might generate a strange result. No effort is made +** to detect invalid UTF8. +** +** At most 4 bytes will be read out of z[]. The return value will always +** be between 1 and 4. +*/ +SQLITE_PRIVATE int sqlite3Utf8ReadLimited( + const u8 *z, + int n, + u32 *piOut +){ + u32 c; + int i = 1; + assert( n>0 ); + c = z[0]; + if( c>=0xc0 ){ + c = sqlite3Utf8Trans1[c-0xc0]; + if( n>4 ) n = 4; + while( irc = SQLITE_INTERRUPT; } #ifndef SQLITE_OMIT_PROGRESS_CALLBACK - if( db->xProgress && (++p->nProgressSteps)>=db->nProgressOps ){ - if( db->xProgress(db->pProgressArg) ){ - p->nErr++; - p->rc = SQLITE_INTERRUPT; + if( db->xProgress ){ + if( p->rc==SQLITE_INTERRUPT ){ + p->nProgressSteps = 0; + }else if( (++p->nProgressSteps)>=db->nProgressOps ){ + if( db->xProgress(db->pProgressArg) ){ + p->nErr++; + p->rc = SQLITE_INTERRUPT; + } + p->nProgressSteps = 0; } - p->nProgressSteps = 0; } #endif } @@ -35185,29 +35601,29 @@ SQLITE_PRIVATE void sqlite3FpDecode(FpDecode *p, double r, int iRound, int mxRou double rr[2]; rr[0] = r; rr[1] = 0.0; - if( rr[0]>1.84e+19 ){ - while( rr[0]>1.84e+119 ){ + if( rr[0]>9.223372036854774784e+18 ){ + while( rr[0]>9.223372036854774784e+118 ){ exp += 100; dekkerMul2(rr, 1.0e-100, -1.99918998026028836196e-117); } - while( rr[0]>1.84e+29 ){ + while( rr[0]>9.223372036854774784e+28 ){ exp += 10; dekkerMul2(rr, 1.0e-10, -3.6432197315497741579e-27); } - while( rr[0]>1.84e+19 ){ + while( rr[0]>9.223372036854774784e+18 ){ exp += 1; dekkerMul2(rr, 1.0e-01, -5.5511151231257827021e-18); } }else{ - while( rr[0]<1.84e-82 ){ + while( rr[0]<9.223372036854774784e-83 ){ exp -= 100; dekkerMul2(rr, 1.0e+100, -1.5902891109759918046e+83); } - while( rr[0]<1.84e+08 ){ + while( rr[0]<9.223372036854774784e+07 ){ exp -= 10; dekkerMul2(rr, 1.0e+10, 0.0); } - while( rr[0]<1.84e+18 ){ + while( rr[0]<9.22337203685477478e+17 ){ exp -= 1; dekkerMul2(rr, 1.0e+01, 0.0); } @@ -35523,121 +35939,32 @@ SQLITE_PRIVATE u8 sqlite3GetVarint(const unsigned char *p, u64 *v){ ** this function assumes the single-byte case has already been handled. */ SQLITE_PRIVATE u8 sqlite3GetVarint32(const unsigned char *p, u32 *v){ - u32 a,b; + u64 v64; + u8 n; - /* The 1-byte case. Overwhelmingly the most common. Handled inline - ** by the getVarin32() macro */ - a = *p; - /* a: p0 (unmasked) */ -#ifndef getVarint32 - if (!(a&0x80)) - { - /* Values between 0 and 127 */ - *v = a; - return 1; - } -#endif + /* Assume that the single-byte case has already been handled by + ** the getVarint32() macro */ + assert( (p[0] & 0x80)!=0 ); - /* The 2-byte case */ - p++; - b = *p; - /* b: p1 (unmasked) */ - if (!(b&0x80)) - { - /* Values between 128 and 16383 */ - a &= 0x7f; - a = a<<7; - *v = a | b; + if( (p[1] & 0x80)==0 ){ + /* This is the two-byte case */ + *v = ((p[0]&0x7f)<<7) | p[1]; return 2; } - - /* The 3-byte case */ - p++; - a = a<<14; - a |= *p; - /* a: p0<<14 | p2 (unmasked) */ - if (!(a&0x80)) - { - /* Values between 16384 and 2097151 */ - a &= (0x7f<<14)|(0x7f); - b &= 0x7f; - b = b<<7; - *v = a | b; + if( (p[2] & 0x80)==0 ){ + /* This is the three-byte case */ + *v = ((p[0]&0x7f)<<14) | ((p[1]&0x7f)<<7) | p[2]; return 3; } - - /* A 32-bit varint is used to store size information in btrees. - ** Objects are rarely larger than 2MiB limit of a 3-byte varint. - ** A 3-byte varint is sufficient, for example, to record the size - ** of a 1048569-byte BLOB or string. - ** - ** We only unroll the first 1-, 2-, and 3- byte cases. The very - ** rare larger cases can be handled by the slower 64-bit varint - ** routine. - */ -#if 1 - { - u64 v64; - u8 n; - - n = sqlite3GetVarint(p-2, &v64); - assert( n>3 && n<=9 ); - if( (v64 & SQLITE_MAX_U32)!=v64 ){ - *v = 0xffffffff; - }else{ - *v = (u32)v64; - } - return n; - } - -#else - /* For following code (kept for historical record only) shows an - ** unrolling for the 3- and 4-byte varint cases. This code is - ** slightly faster, but it is also larger and much harder to test. - */ - p++; - b = b<<14; - b |= *p; - /* b: p1<<14 | p3 (unmasked) */ - if (!(b&0x80)) - { - /* Values between 2097152 and 268435455 */ - b &= (0x7f<<14)|(0x7f); - a &= (0x7f<<14)|(0x7f); - a = a<<7; - *v = a | b; - return 4; - } - - p++; - a = a<<14; - a |= *p; - /* a: p0<<28 | p2<<14 | p4 (unmasked) */ - if (!(a&0x80)) - { - /* Values between 268435456 and 34359738367 */ - a &= SLOT_4_2_0; - b &= SLOT_4_2_0; - b = b<<7; - *v = a | b; - return 5; - } - - /* We can only reach this point when reading a corrupt database - ** file. In that case we are not in any hurry. Use the (relatively - ** slow) general-purpose sqlite3GetVarint() routine to extract the - ** value. */ - { - u64 v64; - u8 n; - - p -= 4; - n = sqlite3GetVarint(p, &v64); - assert( n>5 && n<=9 ); + /* four or more bytes */ + n = sqlite3GetVarint(p, &v64); + assert( n>3 && n<=9 ); + if( (v64 & SQLITE_MAX_U32)!=v64 ){ + *v = 0xffffffff; + }else{ *v = (u32)v64; - return n; } -#endif + return n; } /* @@ -36633,19 +36960,22 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){ /* 171 */ "VCreate" OpHelp(""), /* 172 */ "VDestroy" OpHelp(""), /* 173 */ "VOpen" OpHelp(""), - /* 174 */ "VInitIn" OpHelp("r[P2]=ValueList(P1,P3)"), - /* 175 */ "VColumn" OpHelp("r[P3]=vcolumn(P2)"), - /* 176 */ "VRename" OpHelp(""), - /* 177 */ "Pagecount" OpHelp(""), - /* 178 */ "MaxPgcnt" OpHelp(""), - /* 179 */ "ClrSubtype" OpHelp("r[P1].subtype = 0"), - /* 180 */ "FilterAdd" OpHelp("filter(P1) += key(P3@P4)"), - /* 181 */ "Trace" OpHelp(""), - /* 182 */ "CursorHint" OpHelp(""), - /* 183 */ "ReleaseReg" OpHelp("release r[P1@P2] mask P3"), - /* 184 */ "Noop" OpHelp(""), - /* 185 */ "Explain" OpHelp(""), - /* 186 */ "Abortable" OpHelp(""), + /* 174 */ "VCheck" OpHelp(""), + /* 175 */ "VInitIn" OpHelp("r[P2]=ValueList(P1,P3)"), + /* 176 */ "VColumn" OpHelp("r[P3]=vcolumn(P2)"), + /* 177 */ "VRename" OpHelp(""), + /* 178 */ "Pagecount" OpHelp(""), + /* 179 */ "MaxPgcnt" OpHelp(""), + /* 180 */ "ClrSubtype" OpHelp("r[P1].subtype = 0"), + /* 181 */ "GetSubtype" OpHelp("r[P2] = r[P1].subtype"), + /* 182 */ "SetSubtype" OpHelp("r[P2].subtype = r[P1]"), + /* 183 */ "FilterAdd" OpHelp("filter(P1) += key(P3@P4)"), + /* 184 */ "Trace" OpHelp(""), + /* 185 */ "CursorHint" OpHelp(""), + /* 186 */ "ReleaseReg" OpHelp("release r[P1@P2] mask P3"), + /* 187 */ "Noop" OpHelp(""), + /* 188 */ "Explain" OpHelp(""), + /* 189 */ "Abortable" OpHelp(""), }; return azName[i]; } @@ -40787,9 +41117,6 @@ static int afpUnlock(sqlite3_file *id, int eFileLock) { unixInodeInfo *pInode; afpLockingContext *context = (afpLockingContext *) pFile->lockingContext; int skipShared = 0; -#ifdef SQLITE_TEST - int h = pFile->h; -#endif assert( pFile ); OSTRACE(("UNLOCK %d %d was %d(%d,%d) pid=%d (afp)\n", pFile->h, eFileLock, @@ -40805,9 +41132,6 @@ static int afpUnlock(sqlite3_file *id, int eFileLock) { assert( pInode->nShared!=0 ); if( pFile->eFileLock>SHARED_LOCK ){ assert( pInode->eFileLock==pFile->eFileLock ); - SimulateIOErrorBenign(1); - SimulateIOError( h=(-1) ) - SimulateIOErrorBenign(0); #ifdef SQLITE_DEBUG /* When reducing a lock such that other processes can start @@ -40856,9 +41180,6 @@ static int afpUnlock(sqlite3_file *id, int eFileLock) { unsigned long long sharedLockByte = SHARED_FIRST+pInode->sharedByte; pInode->nShared--; if( pInode->nShared==0 ){ - SimulateIOErrorBenign(1); - SimulateIOError( h=(-1) ) - SimulateIOErrorBenign(0); if( !skipShared ){ rc = afpSetLock(context->dbPath, pFile, sharedLockByte, 1, 0); } @@ -41700,7 +42021,13 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){ #ifdef SQLITE_ENABLE_SETLK_TIMEOUT case SQLITE_FCNTL_LOCK_TIMEOUT: { int iOld = pFile->iBusyTimeout; +#if SQLITE_ENABLE_SETLK_TIMEOUT==1 pFile->iBusyTimeout = *(int*)pArg; +#elif SQLITE_ENABLE_SETLK_TIMEOUT==2 + pFile->iBusyTimeout = !!(*(int*)pArg); +#else +# error "SQLITE_ENABLE_SETLK_TIMEOUT must be set to 1 or 2" +#endif *(int*)pArg = iOld; return SQLITE_OK; } @@ -41953,6 +42280,25 @@ static int unixGetpagesize(void){ ** Either unixShmNode.pShmMutex must be held or unixShmNode.nRef==0 and ** unixMutexHeld() is true when reading or writing any other field ** in this structure. +** +** aLock[SQLITE_SHM_NLOCK]: +** This array records the various locks held by clients on each of the +** SQLITE_SHM_NLOCK slots. If the aLock[] entry is set to 0, then no +** locks are held by the process on this slot. If it is set to -1, then +** some client holds an EXCLUSIVE lock on the locking slot. If the aLock[] +** value is set to a positive value, then it is the number of shared +** locks currently held on the slot. +** +** aMutex[SQLITE_SHM_NLOCK]: +** Normally, when SQLITE_ENABLE_SETLK_TIMEOUT is not defined, mutex +** pShmMutex is used to protect the aLock[] array and the right to +** call fcntl() on unixShmNode.hShm to obtain or release locks. +** +** If SQLITE_ENABLE_SETLK_TIMEOUT is defined though, we use an array +** of mutexes - one for each locking slot. To read or write locking +** slot aLock[iSlot], the caller must hold the corresponding mutex +** aMutex[iSlot]. Similarly, to call fcntl() to obtain or release a +** lock corresponding to slot iSlot, mutex aMutex[iSlot] must be held. */ struct unixShmNode { unixInodeInfo *pInode; /* unixInodeInfo that owns this SHM node */ @@ -41966,10 +42312,11 @@ struct unixShmNode { char **apRegion; /* Array of mapped shared-memory regions */ int nRef; /* Number of unixShm objects pointing to this */ unixShm *pFirst; /* All unixShm objects pointing to this */ +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT + sqlite3_mutex *aMutex[SQLITE_SHM_NLOCK]; +#endif int aLock[SQLITE_SHM_NLOCK]; /* # shared locks on slot, -1==excl lock */ #ifdef SQLITE_DEBUG - u8 exclMask; /* Mask of exclusive locks held */ - u8 sharedMask; /* Mask of shared locks held */ u8 nextShmId; /* Next available unixShm.id value */ #endif }; @@ -42052,16 +42399,35 @@ static int unixShmSystemLock( struct flock f; /* The posix advisory locking structure */ int rc = SQLITE_OK; /* Result code form fcntl() */ - /* Access to the unixShmNode object is serialized by the caller */ pShmNode = pFile->pInode->pShmNode; - assert( pShmNode->nRef==0 || sqlite3_mutex_held(pShmNode->pShmMutex) ); - assert( pShmNode->nRef>0 || unixMutexHeld() ); + + /* Assert that the parameters are within expected range and that the + ** correct mutex or mutexes are held. */ + assert( pShmNode->nRef>=0 ); + assert( (ofst==UNIX_SHM_DMS && n==1) + || (ofst>=UNIX_SHM_BASE && ofst+n<=(UNIX_SHM_BASE+SQLITE_SHM_NLOCK)) + ); + if( ofst==UNIX_SHM_DMS ){ + assert( pShmNode->nRef>0 || unixMutexHeld() ); + assert( pShmNode->nRef==0 || sqlite3_mutex_held(pShmNode->pShmMutex) ); + }else{ +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT + int ii; + for(ii=ofst-UNIX_SHM_BASE; iiaMutex[ii]) ); + } +#else + assert( sqlite3_mutex_held(pShmNode->pShmMutex) ); + assert( pShmNode->nRef>0 ); +#endif + } /* Shared locks never span more than one byte */ assert( n==1 || lockType!=F_RDLCK ); /* Locks are within range */ assert( n>=1 && n<=SQLITE_SHM_NLOCK ); + assert( ofst>=UNIX_SHM_BASE && ofst<=(UNIX_SHM_DMS+SQLITE_SHM_NLOCK) ); if( pShmNode->hShm>=0 ){ int res; @@ -42072,7 +42438,7 @@ static int unixShmSystemLock( f.l_len = n; res = osSetPosixAdvisoryLock(pShmNode->hShm, &f, pFile); if( res==-1 ){ -#ifdef SQLITE_ENABLE_SETLK_TIMEOUT +#if defined(SQLITE_ENABLE_SETLK_TIMEOUT) && SQLITE_ENABLE_SETLK_TIMEOUT==1 rc = (pFile->iBusyTimeout ? SQLITE_BUSY_TIMEOUT : SQLITE_BUSY); #else rc = SQLITE_BUSY; @@ -42080,39 +42446,28 @@ static int unixShmSystemLock( } } - /* Update the global lock state and do debug tracing */ + /* Do debug tracing */ #ifdef SQLITE_DEBUG - { u16 mask; OSTRACE(("SHM-LOCK ")); - mask = ofst>31 ? 0xffff : (1<<(ofst+n)) - (1<exclMask &= ~mask; - pShmNode->sharedMask &= ~mask; + OSTRACE(("unlock %d..%d ok\n", ofst, ofst+n-1)); }else if( lockType==F_RDLCK ){ - OSTRACE(("read-lock %d ok", ofst)); - pShmNode->exclMask &= ~mask; - pShmNode->sharedMask |= mask; + OSTRACE(("read-lock %d..%d ok\n", ofst, ofst+n-1)); }else{ assert( lockType==F_WRLCK ); - OSTRACE(("write-lock %d ok", ofst)); - pShmNode->exclMask |= mask; - pShmNode->sharedMask &= ~mask; + OSTRACE(("write-lock %d..%d ok\n", ofst, ofst+n-1)); } }else{ if( lockType==F_UNLCK ){ - OSTRACE(("unlock %d failed", ofst)); + OSTRACE(("unlock %d..%d failed\n", ofst, ofst+n-1)); }else if( lockType==F_RDLCK ){ - OSTRACE(("read-lock failed")); + OSTRACE(("read-lock %d..%d failed\n", ofst, ofst+n-1)); }else{ assert( lockType==F_WRLCK ); - OSTRACE(("write-lock %d failed", ofst)); + OSTRACE(("write-lock %d..%d failed\n", ofst, ofst+n-1)); } } - OSTRACE((" - afterwards %03x,%03x\n", - pShmNode->sharedMask, pShmNode->exclMask)); - } #endif return rc; @@ -42149,6 +42504,11 @@ static void unixShmPurge(unixFile *pFd){ int i; assert( p->pInode==pFd->pInode ); sqlite3_mutex_free(p->pShmMutex); +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT + for(i=0; iaMutex[i]); + } +#endif for(i=0; inRegion; i+=nShmPerMap){ if( p->hShm>=0 ){ osMunmap(p->apRegion[i], p->szRegion); @@ -42208,7 +42568,20 @@ static int unixLockSharedMemory(unixFile *pDbFd, unixShmNode *pShmNode){ pShmNode->isUnlocked = 1; rc = SQLITE_READONLY_CANTINIT; }else{ +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT + /* Do not use a blocking lock here. If the lock cannot be obtained + ** immediately, it means some other connection is truncating the + ** *-shm file. And after it has done so, it will not release its + ** lock, but only downgrade it to a shared lock. So no point in + ** blocking here. The call below to obtain the shared DMS lock may + ** use a blocking lock. */ + int iSaveTimeout = pDbFd->iBusyTimeout; + pDbFd->iBusyTimeout = 0; +#endif rc = unixShmSystemLock(pDbFd, F_WRLCK, UNIX_SHM_DMS, 1); +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT + pDbFd->iBusyTimeout = iSaveTimeout; +#endif /* The first connection to attach must truncate the -shm file. We ** truncate to 3 bytes (an arbitrary small number, less than the ** -shm header size) rather than 0 as a system debugging aid, to @@ -42329,6 +42702,18 @@ static int unixOpenSharedMemory(unixFile *pDbFd){ rc = SQLITE_NOMEM_BKPT; goto shm_open_err; } +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT + { + int ii; + for(ii=0; iiaMutex[ii] = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); + if( pShmNode->aMutex[ii]==0 ){ + rc = SQLITE_NOMEM_BKPT; + goto shm_open_err; + } + } + } +#endif } if( pInode->bProcessLock==0 ){ @@ -42550,9 +42935,11 @@ shmpage_out: */ #ifdef SQLITE_DEBUG static int assertLockingArrayOk(unixShmNode *pShmNode){ +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT + return 1; +#else unixShm *pX; int aLock[SQLITE_SHM_NLOCK]; - assert( sqlite3_mutex_held(pShmNode->pShmMutex) ); memset(aLock, 0, sizeof(aLock)); for(pX=pShmNode->pFirst; pX; pX=pX->pNext){ @@ -42570,13 +42957,14 @@ static int assertLockingArrayOk(unixShmNode *pShmNode){ assert( 0==memcmp(pShmNode->aLock, aLock, sizeof(aLock)) ); return (memcmp(pShmNode->aLock, aLock, sizeof(aLock))==0); +#endif } #endif /* ** Change the lock state for a shared-memory segment. ** -** Note that the relationship between SHAREd and EXCLUSIVE locks is a little +** Note that the relationship between SHARED and EXCLUSIVE locks is a little ** different here than in posix. In xShmLock(), one can go from unlocked ** to shared and back or from unlocked to exclusive and back. But one may ** not go from shared to exclusive or from exclusive to shared. @@ -42591,7 +42979,7 @@ static int unixShmLock( unixShm *p; /* The shared memory being locked */ unixShmNode *pShmNode; /* The underlying file iNode */ int rc = SQLITE_OK; /* Result code */ - u16 mask; /* Mask of locks to take or release */ + u16 mask = (1<<(ofst+n)) - (1<pShm; @@ -42626,88 +43014,151 @@ static int unixShmLock( ** It is not permitted to block on the RECOVER lock. */ #ifdef SQLITE_ENABLE_SETLK_TIMEOUT - assert( (flags & SQLITE_SHM_UNLOCK) || pDbFd->iBusyTimeout==0 || ( - (ofst!=2) /* not RECOVER */ - && (ofst!=1 || (p->exclMask|p->sharedMask)==0) - && (ofst!=0 || (p->exclMask|p->sharedMask)<3) - && (ofst<3 || (p->exclMask|p->sharedMask)<(1<exclMask|p->sharedMask); + assert( (flags & SQLITE_SHM_UNLOCK) || pDbFd->iBusyTimeout==0 || ( + (ofst!=2) /* not RECOVER */ + && (ofst!=1 || lockMask==0 || lockMask==2) + && (ofst!=0 || lockMask<3) + && (ofst<3 || lockMask<(1<1 || mask==(1<pShmMutex); - assert( assertLockingArrayOk(pShmNode) ); - if( flags & SQLITE_SHM_UNLOCK ){ - if( (p->exclMask|p->sharedMask) & mask ){ - int ii; - int bUnlock = 1; + /* Check if there is any work to do. There are three cases: + ** + ** a) An unlock operation where there are locks to unlock, + ** b) An shared lock where the requested lock is not already held + ** c) An exclusive lock where the requested lock is not already held + ** + ** The SQLite core never requests an exclusive lock that it already holds. + ** This is assert()ed below. + */ + assert( flags!=(SQLITE_SHM_EXCLUSIVE|SQLITE_SHM_LOCK) + || 0==(p->exclMask & mask) + ); + if( ((flags & SQLITE_SHM_UNLOCK) && ((p->exclMask|p->sharedMask) & mask)) + || (flags==(SQLITE_SHM_SHARED|SQLITE_SHM_LOCK) && 0==(p->sharedMask & mask)) + || (flags==(SQLITE_SHM_EXCLUSIVE|SQLITE_SHM_LOCK)) + ){ - for(ii=ofst; ii((p->sharedMask & (1<aMutex[iMutex]); + if( rc!=SQLITE_OK ) goto leave_shmnode_mutexes; + }else{ + sqlite3_mutex_enter(pShmNode->aMutex[iMutex]); } + } +#else + sqlite3_mutex_enter(pShmNode->pShmMutex); +#endif - if( bUnlock ){ - rc = unixShmSystemLock(pDbFd, F_UNLCK, ofst+UNIX_SHM_BASE, n); + if( ALWAYS(rc==SQLITE_OK) ){ + if( flags & SQLITE_SHM_UNLOCK ){ + /* Case (a) - unlock. */ + int bUnlock = 1; + assert( (p->exclMask & p->sharedMask)==0 ); + assert( !(flags & SQLITE_SHM_EXCLUSIVE) || (p->exclMask & mask)==mask ); + assert( !(flags & SQLITE_SHM_SHARED) || (p->sharedMask & mask)==mask ); + + /* If this is a SHARED lock being unlocked, it is possible that other + ** clients within this process are holding the same SHARED lock. In + ** this case, set bUnlock to 0 so that the posix lock is not removed + ** from the file-descriptor below. */ + if( flags & SQLITE_SHM_SHARED ){ + assert( n==1 ); + assert( aLock[ofst]>=1 ); + if( aLock[ofst]>1 ){ + bUnlock = 0; + aLock[ofst]--; + p->sharedMask &= ~mask; + } + } + + if( bUnlock ){ + rc = unixShmSystemLock(pDbFd, F_UNLCK, ofst+UNIX_SHM_BASE, n); + if( rc==SQLITE_OK ){ + memset(&aLock[ofst], 0, sizeof(int)*n); + p->sharedMask &= ~mask; + p->exclMask &= ~mask; + } + } + }else if( flags & SQLITE_SHM_SHARED ){ + /* Case (b) - a shared lock. */ + + if( aLock[ofst]<0 ){ + /* An exclusive lock is held by some other connection. BUSY. */ + rc = SQLITE_BUSY; + }else if( aLock[ofst]==0 ){ + rc = unixShmSystemLock(pDbFd, F_RDLCK, ofst+UNIX_SHM_BASE, n); + } + + /* Get the local shared locks */ if( rc==SQLITE_OK ){ - memset(&aLock[ofst], 0, sizeof(int)*n); + p->sharedMask |= mask; + aLock[ofst]++; } - }else if( ALWAYS(p->sharedMask & (1<1 ); - aLock[ofst]--; - } + }else{ + /* Case (c) - an exclusive lock. */ + int ii; - /* Undo the local locks */ - if( rc==SQLITE_OK ){ - p->exclMask &= ~mask; - p->sharedMask &= ~mask; - } - } - }else if( flags & SQLITE_SHM_SHARED ){ - assert( n==1 ); - assert( (p->exclMask & (1<sharedMask & mask)==0 ){ - if( aLock[ofst]<0 ){ - rc = SQLITE_BUSY; - }else if( aLock[ofst]==0 ){ - rc = unixShmSystemLock(pDbFd, F_RDLCK, ofst+UNIX_SHM_BASE, n); - } - - /* Get the local shared locks */ - if( rc==SQLITE_OK ){ - p->sharedMask |= mask; - aLock[ofst]++; - } - } - }else{ - /* Make sure no sibling connections hold locks that will block this - ** lock. If any do, return SQLITE_BUSY right away. */ - int ii; - for(ii=ofst; iisharedMask & mask)==0 ); - if( ALWAYS((p->exclMask & (1<sharedMask & mask)==0 ); - p->exclMask |= mask; + assert( (p->exclMask & mask)==0 ); + + /* Make sure no sibling connections hold locks that will block this + ** lock. If any do, return SQLITE_BUSY right away. */ for(ii=ofst; iiexclMask |= mask; + for(ii=ofst; ii=ofst; iMutex--){ + sqlite3_mutex_leave(pShmNode->aMutex[iMutex]); + } +#else + sqlite3_mutex_leave(pShmNode->pShmMutex); +#endif } - assert( assertLockingArrayOk(pShmNode) ); - sqlite3_mutex_leave(pShmNode->pShmMutex); + OSTRACE(("SHM-LOCK shmid-%d, pid-%d got %03x,%03x\n", p->id, osGetpid(0), p->sharedMask, p->exclMask)); return rc; @@ -42957,11 +43408,16 @@ static int unixFetch(sqlite3_file *fd, i64 iOff, int nAmt, void **pp){ #if SQLITE_MAX_MMAP_SIZE>0 if( pFd->mmapSizeMax>0 ){ + /* Ensure that there is always at least a 256 byte buffer of addressable + ** memory following the returned page. If the database is corrupt, + ** SQLite may overread the page slightly (in practice only a few bytes, + ** but 256 is safe, round, number). */ + const int nEofBuffer = 256; if( pFd->pMapRegion==0 ){ int rc = unixMapfile(pFd, -1); if( rc!=SQLITE_OK ) return rc; } - if( pFd->mmapSize >= iOff+nAmt ){ + if( pFd->mmapSize >= (iOff+nAmt+nEofBuffer) ){ *pp = &((u8 *)pFd->pMapRegion)[iOff]; pFd->nFetchOut++; } @@ -50314,6 +50770,11 @@ static int winFetch(sqlite3_file *fd, i64 iOff, int nAmt, void **pp){ #if SQLITE_MAX_MMAP_SIZE>0 if( pFd->mmapSizeMax>0 ){ + /* Ensure that there is always at least a 256 byte buffer of addressable + ** memory following the returned page. If the database is corrupt, + ** SQLite may overread the page slightly (in practice only a few bytes, + ** but 256 is safe, round, number). */ + const int nEofBuffer = 256; if( pFd->pMapRegion==0 ){ int rc = winMapfile(pFd, -1); if( rc!=SQLITE_OK ){ @@ -50322,7 +50783,7 @@ static int winFetch(sqlite3_file *fd, i64 iOff, int nAmt, void **pp){ return rc; } } - if( pFd->mmapSize >= iOff+nAmt ){ + if( pFd->mmapSize >= (iOff+nAmt+nEofBuffer) ){ assert( pFd->pMapRegion!=0 ); *pp = &((u8 *)pFd->pMapRegion)[iOff]; pFd->nFetchOut++; @@ -56925,7 +57386,7 @@ struct Pager { char *zJournal; /* Name of the journal file */ int (*xBusyHandler)(void*); /* Function to call when busy */ void *pBusyHandlerArg; /* Context argument for xBusyHandler */ - int aStat[4]; /* Total cache hits, misses, writes, spills */ + u32 aStat[4]; /* Total cache hits, misses, writes, spills */ #ifdef SQLITE_TEST int nRead; /* Database pages read */ #endif @@ -57055,9 +57516,8 @@ SQLITE_PRIVATE int sqlite3PagerDirectReadOk(Pager *pPager, Pgno pgno){ #ifndef SQLITE_OMIT_WAL if( pPager->pWal ){ u32 iRead = 0; - int rc; - rc = sqlite3WalFindFrame(pPager->pWal, pgno, &iRead); - return (rc==SQLITE_OK && iRead==0); + (void)sqlite3WalFindFrame(pPager->pWal, pgno, &iRead); + return iRead==0; } #endif return 1; @@ -57729,9 +58189,32 @@ static int writeJournalHdr(Pager *pPager){ memset(zHeader, 0, sizeof(aJournalMagic)+4); } + + /* The random check-hash initializer */ - sqlite3_randomness(sizeof(pPager->cksumInit), &pPager->cksumInit); + if( pPager->journalMode!=PAGER_JOURNALMODE_MEMORY ){ + sqlite3_randomness(sizeof(pPager->cksumInit), &pPager->cksumInit); + } +#ifdef SQLITE_DEBUG + else{ + /* The Pager.cksumInit variable is usually randomized above to protect + ** against there being existing records in the journal file. This is + ** dangerous, as following a crash they may be mistaken for records + ** written by the current transaction and rolled back into the database + ** file, causing corruption. The following assert statements verify + ** that this is not required in "journal_mode=memory" mode, as in that + ** case the journal file is always 0 bytes in size at this point. + ** It is advantageous to avoid the sqlite3_randomness() call if possible + ** as it takes the global PRNG mutex. */ + i64 sz = 0; + sqlite3OsFileSize(pPager->jfd, &sz); + assert( sz==0 ); + assert( pPager->journalOff==journalHdrOffset(pPager) ); + assert( sqlite3JournalIsInMemory(pPager->jfd) ); + } +#endif put32bits(&zHeader[sizeof(aJournalMagic)+4], pPager->cksumInit); + /* The initial database size */ put32bits(&zHeader[sizeof(aJournalMagic)+8], pPager->dbOrigSize); /* The assumed sector size for this process */ @@ -58375,6 +58858,9 @@ static int pager_end_transaction(Pager *pPager, int hasSuper, int bCommit){ return (rc==SQLITE_OK?rc2:rc); } +/* Forward reference */ +static int pager_playback(Pager *pPager, int isHot); + /* ** Execute a rollback if a transaction is active and unlock the ** database file. @@ -58403,6 +58889,21 @@ static void pagerUnlockAndRollback(Pager *pPager){ assert( pPager->eState==PAGER_READER ); pager_end_transaction(pPager, 0, 0); } + }else if( pPager->eState==PAGER_ERROR + && pPager->journalMode==PAGER_JOURNALMODE_MEMORY + && isOpen(pPager->jfd) + ){ + /* Special case for a ROLLBACK due to I/O error with an in-memory + ** journal: We have to rollback immediately, before the journal is + ** closed, because once it is closed, all content is forgotten. */ + int errCode = pPager->errCode; + u8 eLock = pPager->eLock; + pPager->eState = PAGER_OPEN; + pPager->errCode = SQLITE_OK; + pPager->eLock = EXCLUSIVE_LOCK; + pager_playback(pPager, 1); + pPager->errCode = errCode; + pPager->eLock = eLock; } pager_unlock(pPager); } @@ -61258,10 +61759,13 @@ act_like_temp_file: */ SQLITE_API sqlite3_file *sqlite3_database_file_object(const char *zName){ Pager *pPager; + const char *p; while( zName[-1]!=0 || zName[-2]!=0 || zName[-3]!=0 || zName[-4]!=0 ){ zName--; } - pPager = *(Pager**)(zName - 4 - sizeof(Pager*)); + p = zName - 4 - sizeof(Pager*); + assert( EIGHT_BYTE_ALIGNMENT(p) ); + pPager = *(Pager**)p; return pPager->fd; } @@ -61895,8 +62399,20 @@ SQLITE_PRIVATE int sqlite3PagerGet( DbPage **ppPage, /* Write a pointer to the page here */ int flags /* PAGER_GET_XXX flags */ ){ - /* printf("PAGE %u\n", pgno); fflush(stdout); */ +#if 0 /* Trace page fetch by setting to 1 */ + int rc; + printf("PAGE %u\n", pgno); + fflush(stdout); + rc = pPager->xGet(pPager, pgno, ppPage, flags); + if( rc ){ + printf("PAGE %u failed with 0x%02x\n", pgno, rc); + fflush(stdout); + } + return rc; +#else + /* Normal, high-speed version of sqlite3PagerGet() */ return pPager->xGet(pPager, pgno, ppPage, flags); +#endif } /* @@ -62772,6 +63288,13 @@ SQLITE_PRIVATE int sqlite3PagerCommitPhaseOne( rc = sqlite3OsFileControl(fd, SQLITE_FCNTL_BEGIN_ATOMIC_WRITE, 0); if( rc==SQLITE_OK ){ rc = pager_write_pagelist(pPager, pList); + if( rc==SQLITE_OK && pPager->dbSize>pPager->dbFileSize ){ + char *pTmp = pPager->pTmpSpace; + int szPage = (int)pPager->pageSize; + memset(pTmp, 0, szPage); + rc = sqlite3OsWrite(pPager->fd, pTmp, szPage, + ((i64)pPager->dbSize*pPager->pageSize)-szPage); + } if( rc==SQLITE_OK ){ rc = sqlite3OsFileControl(fd, SQLITE_FCNTL_COMMIT_ATOMIC_WRITE, 0); } @@ -63006,11 +63529,11 @@ SQLITE_PRIVATE int *sqlite3PagerStats(Pager *pPager){ a[3] = pPager->eState==PAGER_OPEN ? -1 : (int) pPager->dbSize; a[4] = pPager->eState; a[5] = pPager->errCode; - a[6] = pPager->aStat[PAGER_STAT_HIT]; - a[7] = pPager->aStat[PAGER_STAT_MISS]; + a[6] = (int)pPager->aStat[PAGER_STAT_HIT] & 0x7fffffff; + a[7] = (int)pPager->aStat[PAGER_STAT_MISS] & 0x7fffffff; a[8] = 0; /* Used to be pPager->nOvfl */ a[9] = pPager->nRead; - a[10] = pPager->aStat[PAGER_STAT_WRITE]; + a[10] = (int)pPager->aStat[PAGER_STAT_WRITE] & 0x7fffffff; return a; } #endif @@ -63026,7 +63549,7 @@ SQLITE_PRIVATE int *sqlite3PagerStats(Pager *pPager){ ** reset parameter is non-zero, the cache hit or miss count is zeroed before ** returning. */ -SQLITE_PRIVATE void sqlite3PagerCacheStat(Pager *pPager, int eStat, int reset, int *pnVal){ +SQLITE_PRIVATE void sqlite3PagerCacheStat(Pager *pPager, int eStat, int reset, u64 *pnVal){ assert( eStat==SQLITE_DBSTATUS_CACHE_HIT || eStat==SQLITE_DBSTATUS_CACHE_MISS @@ -63583,7 +64106,7 @@ SQLITE_PRIVATE int sqlite3PagerSetJournalMode(Pager *pPager, int eMode){ } assert( state==pPager->eState ); } - }else if( eMode==PAGER_JOURNALMODE_OFF ){ + }else if( eMode==PAGER_JOURNALMODE_OFF || eMode==PAGER_JOURNALMODE_MEMORY ){ sqlite3OsClose(pPager->jfd); } } @@ -63966,7 +64489,7 @@ SQLITE_PRIVATE int sqlite3PagerWalFramesize(Pager *pPager){ } #endif -#ifdef SQLITE_USE_SEH +#if defined(SQLITE_USE_SEH) && !defined(SQLITE_OMIT_WAL) SQLITE_PRIVATE int sqlite3PagerWalSystemErrno(Pager *pPager){ return sqlite3WalSystemErrno(pPager->pWal); } @@ -65982,6 +66505,19 @@ static int walIteratorInit(Wal *pWal, u32 nBackfill, WalIterator **pp){ } #ifdef SQLITE_ENABLE_SETLK_TIMEOUT + + +/* +** Attempt to enable blocking locks that block for nMs ms. Return 1 if +** blocking locks are successfully enabled, or 0 otherwise. +*/ +static int walEnableBlockingMs(Wal *pWal, int nMs){ + int rc = sqlite3OsFileControl( + pWal->pDbFd, SQLITE_FCNTL_LOCK_TIMEOUT, (void*)&nMs + ); + return (rc==SQLITE_OK); +} + /* ** Attempt to enable blocking locks. Blocking locks are enabled only if (a) ** they are supported by the VFS, and (b) the database handle is configured @@ -65993,11 +66529,7 @@ static int walEnableBlocking(Wal *pWal){ if( pWal->db ){ int tmout = pWal->db->busyTimeout; if( tmout ){ - int rc; - rc = sqlite3OsFileControl( - pWal->pDbFd, SQLITE_FCNTL_LOCK_TIMEOUT, (void*)&tmout - ); - res = (rc==SQLITE_OK); + res = walEnableBlockingMs(pWal, tmout); } } return res; @@ -66046,20 +66578,10 @@ SQLITE_PRIVATE void sqlite3WalDb(Wal *pWal, sqlite3 *db){ pWal->db = db; } -/* -** Take an exclusive WRITE lock. Blocking if so configured. -*/ -static int walLockWriter(Wal *pWal){ - int rc; - walEnableBlocking(pWal); - rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1); - walDisableBlocking(pWal); - return rc; -} #else # define walEnableBlocking(x) 0 # define walDisableBlocking(x) -# define walLockWriter(pWal) walLockExclusive((pWal), WAL_WRITE_LOCK, 1) +# define walEnableBlockingMs(pWal, ms) 0 # define sqlite3WalDb(pWal, db) #endif /* ifdef SQLITE_ENABLE_SETLK_TIMEOUT */ @@ -66660,7 +67182,9 @@ static int walIndexReadHdr(Wal *pWal, int *pChanged){ } }else{ int bWriteLock = pWal->writeLock; - if( bWriteLock || SQLITE_OK==(rc = walLockWriter(pWal)) ){ + if( bWriteLock + || SQLITE_OK==(rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1)) + ){ pWal->writeLock = 1; if( SQLITE_OK==(rc = walIndexPage(pWal, 0, &page0)) ){ badHdr = walIndexTryHdr(pWal, pChanged); @@ -66668,7 +67192,8 @@ static int walIndexReadHdr(Wal *pWal, int *pChanged){ /* If the wal-index header is still malformed even while holding ** a WRITE lock, it can only mean that the header is corrupted and ** needs to be reconstructed. So run recovery to do exactly that. - */ + ** Disable blocking locks first. */ + walDisableBlocking(pWal); rc = walIndexRecover(pWal); *pChanged = 1; } @@ -66878,6 +67403,37 @@ static int walBeginShmUnreliable(Wal *pWal, int *pChanged){ return rc; } +/* +** The final argument passed to walTryBeginRead() is of type (int*). The +** caller should invoke walTryBeginRead as follows: +** +** int cnt = 0; +** do { +** rc = walTryBeginRead(..., &cnt); +** }while( rc==WAL_RETRY ); +** +** The final value of "cnt" is of no use to the caller. It is used by +** the implementation of walTryBeginRead() as follows: +** +** + Each time walTryBeginRead() is called, it is incremented. Once +** it reaches WAL_RETRY_PROTOCOL_LIMIT - indicating that walTryBeginRead() +** has many times been invoked and failed with WAL_RETRY - walTryBeginRead() +** returns SQLITE_PROTOCOL. +** +** + If SQLITE_ENABLE_SETLK_TIMEOUT is defined and walTryBeginRead() failed +** because a blocking lock timed out (SQLITE_BUSY_TIMEOUT from the OS +** layer), the WAL_RETRY_BLOCKED_MASK bit is set in "cnt". In this case +** the next invocation of walTryBeginRead() may omit an expected call to +** sqlite3OsSleep(). There has already been a delay when the previous call +** waited on a lock. +*/ +#define WAL_RETRY_PROTOCOL_LIMIT 100 +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT +# define WAL_RETRY_BLOCKED_MASK 0x10000000 +#else +# define WAL_RETRY_BLOCKED_MASK 0 +#endif + /* ** Attempt to start a read transaction. This might fail due to a race or ** other transient condition. When that happens, it returns WAL_RETRY to @@ -66928,13 +67484,16 @@ static int walBeginShmUnreliable(Wal *pWal, int *pChanged){ ** so it takes care to hold an exclusive lock on the corresponding ** WAL_READ_LOCK() while changing values. */ -static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){ +static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int *pCnt){ volatile WalCkptInfo *pInfo; /* Checkpoint information in wal-index */ u32 mxReadMark; /* Largest aReadMark[] value */ int mxI; /* Index of largest aReadMark[] value */ int i; /* Loop counter */ int rc = SQLITE_OK; /* Return code */ u32 mxFrame; /* Wal frame to lock to */ +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT + int nBlockTmout = 0; +#endif assert( pWal->readLock<0 ); /* Not currently locked */ @@ -66958,14 +67517,34 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){ ** so that on the 100th (and last) RETRY we delay for 323 milliseconds. ** The total delay time before giving up is less than 10 seconds. */ - if( cnt>5 ){ + (*pCnt)++; + if( *pCnt>5 ){ int nDelay = 1; /* Pause time in microseconds */ - if( cnt>100 ){ + int cnt = (*pCnt & ~WAL_RETRY_BLOCKED_MASK); + if( cnt>WAL_RETRY_PROTOCOL_LIMIT ){ VVA_ONLY( pWal->lockError = 1; ) return SQLITE_PROTOCOL; } - if( cnt>=10 ) nDelay = (cnt-9)*(cnt-9)*39; + if( *pCnt>=10 ) nDelay = (cnt-9)*(cnt-9)*39; +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT + /* In SQLITE_ENABLE_SETLK_TIMEOUT builds, configure the file-descriptor + ** to block for locks for approximately nDelay us. This affects three + ** locks: (a) the shared lock taken on the DMS slot in os_unix.c (if + ** using os_unix.c), (b) the WRITER lock taken in walIndexReadHdr() if the + ** first attempted read fails, and (c) the shared lock taken on the + ** read-mark. + ** + ** If the previous call failed due to an SQLITE_BUSY_TIMEOUT error, + ** then sleep for the minimum of 1us. The previous call already provided + ** an extra delay while it was blocking on the lock. + */ + nBlockTmout = (nDelay+998) / 1000; + if( !useWal && walEnableBlockingMs(pWal, nBlockTmout) ){ + if( *pCnt & WAL_RETRY_BLOCKED_MASK ) nDelay = 1; + } +#endif sqlite3OsSleep(pWal->pVfs, nDelay); + *pCnt &= ~WAL_RETRY_BLOCKED_MASK; } if( !useWal ){ @@ -66973,6 +67552,13 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){ if( pWal->bShmUnreliable==0 ){ rc = walIndexReadHdr(pWal, pChanged); } +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT + walDisableBlocking(pWal); + if( rc==SQLITE_BUSY_TIMEOUT ){ + rc = SQLITE_BUSY; + *pCnt |= WAL_RETRY_BLOCKED_MASK; + } +#endif if( rc==SQLITE_BUSY ){ /* If there is not a recovery running in another thread or process ** then convert BUSY errors to WAL_RETRY. If recovery is known to @@ -67087,9 +67673,19 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){ return rc==SQLITE_BUSY ? WAL_RETRY : SQLITE_READONLY_CANTINIT; } + (void)walEnableBlockingMs(pWal, nBlockTmout); rc = walLockShared(pWal, WAL_READ_LOCK(mxI)); + walDisableBlocking(pWal); if( rc ){ - return rc==SQLITE_BUSY ? WAL_RETRY : rc; +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT + if( rc==SQLITE_BUSY_TIMEOUT ){ + *pCnt |= WAL_RETRY_BLOCKED_MASK; + } +#else + assert( rc!=SQLITE_BUSY_TIMEOUT ); +#endif + assert( (rc&0xFF)!=SQLITE_BUSY||rc==SQLITE_BUSY||rc==SQLITE_BUSY_TIMEOUT ); + return (rc&0xFF)==SQLITE_BUSY ? WAL_RETRY : rc; } /* Now that the read-lock has been obtained, check that neither the ** value in the aReadMark[] array or the contents of the wal-index @@ -67277,7 +67873,7 @@ static int walBeginReadTransaction(Wal *pWal, int *pChanged){ #endif do{ - rc = walTryBeginRead(pWal, pChanged, 0, ++cnt); + rc = walTryBeginRead(pWal, pChanged, 0, &cnt); }while( rc==WAL_RETRY ); testcase( (rc&0xff)==SQLITE_BUSY ); testcase( (rc&0xff)==SQLITE_IOERR ); @@ -67458,6 +68054,7 @@ static int walFindFrame( iRead = iFrame; } if( (nCollide--)==0 ){ + *piRead = 0; return SQLITE_CORRUPT_BKPT; } iKey = walNextHash(iKey); @@ -67761,7 +68358,7 @@ static int walRestartLog(Wal *pWal){ cnt = 0; do{ int notUsed; - rc = walTryBeginRead(pWal, ¬Used, 1, ++cnt); + rc = walTryBeginRead(pWal, ¬Used, 1, &cnt); }while( rc==WAL_RETRY ); assert( (rc&0xff)!=SQLITE_BUSY ); /* BUSY not possible when useWal==1 */ testcase( (rc&0xff)==SQLITE_IOERR ); @@ -68182,10 +68779,9 @@ SQLITE_PRIVATE int sqlite3WalCheckpoint( if( pWal->readOnly ) return SQLITE_READONLY; WALTRACE(("WAL%p: checkpoint begins\n", pWal)); - /* Enable blocking locks, if possible. If blocking locks are successfully - ** enabled, set xBusy2=0 so that the busy-handler is never invoked. */ + /* Enable blocking locks, if possible. */ sqlite3WalDb(pWal, db); - (void)walEnableBlocking(pWal); + if( xBusy2 ) (void)walEnableBlocking(pWal); /* IMPLEMENTATION-OF: R-62028-47212 All calls obtain an exclusive ** "checkpoint" lock on the database file. @@ -68226,9 +68822,14 @@ SQLITE_PRIVATE int sqlite3WalCheckpoint( /* Read the wal-index header. */ SEH_TRY { if( rc==SQLITE_OK ){ + /* For a passive checkpoint, do not re-enable blocking locks after + ** reading the wal-index header. A passive checkpoint should not block + ** or invoke the busy handler. The only lock such a checkpoint may + ** attempt to obtain is a lock on a read-slot, and it should give up + ** immediately and do a partial checkpoint if it cannot obtain it. */ walDisableBlocking(pWal); rc = walIndexReadHdr(pWal, &isChanged); - (void)walEnableBlocking(pWal); + if( eMode2!=SQLITE_CHECKPOINT_PASSIVE ) (void)walEnableBlocking(pWal); if( isChanged && pWal->pDbFd->pMethods->iVersion>=3 ){ sqlite3OsUnfetch(pWal->pDbFd, 0, 0); } @@ -68565,7 +69166,7 @@ SQLITE_PRIVATE sqlite3_file *sqlite3WalFile(Wal *pWal){ ** 22 1 Min embedded payload fraction (must be 32) ** 23 1 Min leaf payload fraction (must be 32) ** 24 4 File change counter -** 28 4 Reserved for future use +** 28 4 The size of the database in pages ** 32 4 First freelist page ** 36 4 Number of freelist pages in the file ** 40 60 15 4-byte meta values passed to higher layers @@ -69196,7 +69797,7 @@ struct IntegrityCk { BtShared *pBt; /* The tree being checked out */ Pager *pPager; /* The associated pager. Also accessible by pBt->pPager */ u8 *aPgRef; /* 1 bit per page in the db (see above) */ - Pgno nPage; /* Number of pages in the database */ + Pgno nCkPage; /* Pages in the database. 0 for partial check */ int mxErr; /* Stop accumulating errors when this reaches zero */ int nErr; /* Number of messages written to zErrMsg so far */ int rc; /* SQLITE_OK, SQLITE_NOMEM, or SQLITE_INTERRUPT */ @@ -69529,7 +70130,6 @@ SQLITE_PRIVATE void sqlite3BtreeLeaveCursor(BtCursor *pCur){ /************** End of btmutex.c *********************************************/ /************** Begin file btree.c *******************************************/ - /* ** 2004 April 6 ** @@ -74693,7 +75293,6 @@ static int accessPayload( assert( aWrite>=pBufStart ); /* due to (6) */ memcpy(aSave, aWrite, 4); rc = sqlite3OsRead(fd, aWrite, a+4, (i64)pBt->pageSize*(nextPage-1)); - if( rc && nextPage>pBt->nPage ) rc = SQLITE_CORRUPT_BKPT; nextPage = get4byte(aWrite); memcpy(aWrite, aSave, 4); }else @@ -75813,7 +76412,10 @@ static SQLITE_NOINLINE int btreePrevious(BtCursor *pCur){ } pPage = pCur->pPage; - assert( pPage->isInit ); + if( sqlite3FaultSim(412) ) pPage->isInit = 0; + if( !pPage->isInit ){ + return SQLITE_CORRUPT_BKPT; + } if( !pPage->leaf ){ int idx = pCur->ix; rc = moveToChild(pCur, get4byte(findCell(pPage, idx))); @@ -77024,9 +77626,10 @@ static int rebuildPage( int k; /* Current slot in pCArray->apEnd[] */ u8 *pSrcEnd; /* Current pCArray->apEnd[k] value */ + assert( nCell>0 ); assert( i(u32)usableSize) ){ j = 0; } + if( j>(u32)usableSize ){ j = 0; } memcpy(&pTmp[j], &aData[j], usableSize - j); for(k=0; ALWAYS(kixNx[k]<=i; k++){} @@ -77330,6 +77933,7 @@ static int editPage( return SQLITE_OK; editpage_fail: /* Unable to edit this page. Rebuild it from scratch instead. */ + if( nNew<1 ) return SQLITE_CORRUPT_BKPT; populateCellCache(pCArray, iNew, nNew); return rebuildPage(pCArray, iNew, nNew, pPg); } @@ -79989,7 +80593,8 @@ static void checkAppendMsg( ** corresponds to page iPg is already set. */ static int getPageReferenced(IntegrityCk *pCheck, Pgno iPg){ - assert( iPg<=pCheck->nPage && sizeof(pCheck->aPgRef[0])==1 ); + assert( pCheck->aPgRef!=0 ); + assert( iPg<=pCheck->nCkPage && sizeof(pCheck->aPgRef[0])==1 ); return (pCheck->aPgRef[iPg/8] & (1 << (iPg & 0x07))); } @@ -79997,7 +80602,8 @@ static int getPageReferenced(IntegrityCk *pCheck, Pgno iPg){ ** Set the bit in the IntegrityCk.aPgRef[] array that corresponds to page iPg. */ static void setPageReferenced(IntegrityCk *pCheck, Pgno iPg){ - assert( iPg<=pCheck->nPage && sizeof(pCheck->aPgRef[0])==1 ); + assert( pCheck->aPgRef!=0 ); + assert( iPg<=pCheck->nCkPage && sizeof(pCheck->aPgRef[0])==1 ); pCheck->aPgRef[iPg/8] |= (1 << (iPg & 0x07)); } @@ -80011,7 +80617,7 @@ static void setPageReferenced(IntegrityCk *pCheck, Pgno iPg){ ** Also check that the page number is in bounds. */ static int checkRef(IntegrityCk *pCheck, Pgno iPage){ - if( iPage>pCheck->nPage || iPage==0 ){ + if( iPage>pCheck->nCkPage || iPage==0 ){ checkAppendMsg(pCheck, "invalid page number %u", iPage); return 1; } @@ -80238,6 +80844,7 @@ static int checkTreePage( if( (rc = btreeGetPage(pBt, iPage, &pPage, 0))!=0 ){ checkAppendMsg(pCheck, "unable to get the page. error code=%d", rc); + if( rc==SQLITE_IOERR_NOMEM ) pCheck->rc = SQLITE_NOMEM; goto end_of_check; } @@ -80508,15 +81115,15 @@ SQLITE_PRIVATE int sqlite3BtreeIntegrityCheck( sCheck.db = db; sCheck.pBt = pBt; sCheck.pPager = pBt->pPager; - sCheck.nPage = btreePagecount(sCheck.pBt); + sCheck.nCkPage = btreePagecount(sCheck.pBt); sCheck.mxErr = mxErr; sqlite3StrAccumInit(&sCheck.errMsg, 0, zErr, sizeof(zErr), SQLITE_MAX_LENGTH); sCheck.errMsg.printfFlags = SQLITE_PRINTF_INTERNAL; - if( sCheck.nPage==0 ){ + if( sCheck.nCkPage==0 ){ goto integrity_ck_cleanup; } - sCheck.aPgRef = sqlite3MallocZero((sCheck.nPage / 8)+ 1); + sCheck.aPgRef = sqlite3MallocZero((sCheck.nCkPage / 8)+ 1); if( !sCheck.aPgRef ){ checkOom(&sCheck); goto integrity_ck_cleanup; @@ -80528,7 +81135,7 @@ SQLITE_PRIVATE int sqlite3BtreeIntegrityCheck( } i = PENDING_BYTE_PAGE(pBt); - if( i<=sCheck.nPage ) setPageReferenced(&sCheck, i); + if( i<=sCheck.nCkPage ) setPageReferenced(&sCheck, i); /* Check the integrity of the freelist */ @@ -80579,7 +81186,7 @@ SQLITE_PRIVATE int sqlite3BtreeIntegrityCheck( /* Make sure every page in the file is referenced */ if( !bPartial ){ - for(i=1; i<=sCheck.nPage && sCheck.mxErr; i++){ + for(i=1; i<=sCheck.nCkPage && sCheck.mxErr; i++){ #ifdef SQLITE_OMIT_AUTOVACUUM if( getPageReferenced(&sCheck, i)==0 ){ checkAppendMsg(&sCheck, "Page %u: never used", i); @@ -82020,7 +82627,7 @@ SQLITE_PRIVATE void sqlite3VdbeMemZeroTerminateIfAble(Mem *pMem){ pMem->flags |= MEM_Term; return; } - if( pMem->xDel==(void(*)(void*))sqlite3RCStrUnref ){ + if( pMem->xDel==sqlite3RCStrUnref ){ /* Blindly assume that all RCStr objects are zero-terminated */ pMem->flags |= MEM_Term; return; @@ -83199,7 +83806,7 @@ static int valueFromFunction( #endif assert( pFunc ); if( (pFunc->funcFlags & (SQLITE_FUNC_CONSTANT|SQLITE_FUNC_SLOCHNG))==0 - || (pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL) + || (pFunc->funcFlags & (SQLITE_FUNC_NEEDCOLL|SQLITE_FUNC_RUNONLY))!=0 ){ return SQLITE_OK; } @@ -83400,6 +84007,7 @@ static int valueFromExpr( if( pVal ){ pVal->flags = MEM_Int; pVal->u.i = pExpr->u.zToken[4]==0; + sqlite3ValueApplyAffinity(pVal, affinity, enc); } } @@ -83922,10 +84530,11 @@ static int growOpArray(Vdbe *v, int nOp){ ** sqlite3CantopenError(lineno) */ static void test_addop_breakpoint(int pc, Op *pOp){ - static int n = 0; + static u64 n = 0; (void)pc; (void)pOp; n++; + if( n==LARGEST_UINT64 ) abort(); /* so that n is used, preventing a warning */ } #endif @@ -84713,6 +85322,10 @@ SQLITE_PRIVATE void sqlite3VdbeNoJumpsOutsideSubrtn( int iDest = pOp->p2; /* Jump destination */ if( iDest==0 ) continue; if( pOp->opcode==OP_Gosub ) continue; + if( pOp->p3==20230325 && pOp->opcode==OP_NotNull ){ + /* This is a deliberately taken illegal branch. tag-20230325-2 */ + continue; + } if( iDest<0 ){ int j = ADDR(iDest); assert( j>=0 ); @@ -85106,6 +85719,10 @@ static void freeP4(sqlite3 *db, int p4type, void *p4){ if( db->pnBytesFreed==0 ) sqlite3VtabUnlock((VTable *)p4); break; } + case P4_TABLEREF: { + if( db->pnBytesFreed==0 ) sqlite3DeleteTable(db, (Table*)p4); + break; + } } } @@ -85233,7 +85850,7 @@ static void SQLITE_NOINLINE vdbeChangeP4Full( int n ){ if( pOp->p4type ){ - freeP4(p->db, pOp->p4type, pOp->p4.p); + assert( pOp->p4type > P4_FREE_IF_LE ); pOp->p4type = 0; pOp->p4.p = 0; } @@ -88172,20 +88789,33 @@ SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3BlobCompare(const Mem *pB1, const Mem return n1 - n2; } +/* The following two functions are used only within testcase() to prove +** test coverage. These functions do no exist for production builds. +** We must use separate SQLITE_NOINLINE functions here, since otherwise +** optimizer code movement causes gcov to become very confused. +*/ +#if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_DEBUG) +static int SQLITE_NOINLINE doubleLt(double a, double b){ return a8 ){ + if( sqlite3IsNaN(r) ){ + /* SQLite considers NaN to be a NULL. And all integer values are greater + ** than NULL */ + return 1; + } + if( sqlite3Config.bUseLongDouble ){ LONGDOUBLE_TYPE x = (LONGDOUBLE_TYPE)i; testcase( xr ); testcase( x==r ); - if( xr ) return +1; /*NO_TEST*/ /* work around bugs in gcov */ - return 0; /*NO_TEST*/ /* work around bugs in gcov */ + return (xr); }else{ i64 y; double s; @@ -88195,9 +88825,10 @@ SQLITE_PRIVATE int sqlite3IntFloatCompare(i64 i, double r){ if( iy ) return +1; s = (double)i; - if( sr ) return +1; - return 0; + testcase( doubleLt(s,r) ); + testcase( doubleLt(r,s) ); + testcase( doubleEq(r,s) ); + return (sr); } } @@ -89342,7 +89973,15 @@ SQLITE_API int sqlite3_clear_bindings(sqlite3_stmt *pStmt){ int rc = SQLITE_OK; Vdbe *p = (Vdbe*)pStmt; #if SQLITE_THREADSAFE - sqlite3_mutex *mutex = ((Vdbe*)pStmt)->db->mutex; + sqlite3_mutex *mutex; +#endif +#ifdef SQLITE_ENABLE_API_ARMOR + if( pStmt==0 ){ + return SQLITE_MISUSE_BKPT; + } +#endif +#if SQLITE_THREADSAFE + mutex = p->db->mutex; #endif sqlite3_mutex_enter(mutex); for(i=0; inVar; i++){ @@ -89565,7 +90204,7 @@ SQLITE_API void sqlite3_value_free(sqlite3_value *pOld){ ** is too big or if an OOM occurs. ** ** The invokeValueDestructor(P,X) routine invokes destructor function X() -** on value P is not going to be used and need to be destroyed. +** on value P if P is not going to be used and need to be destroyed. */ static void setResultStrOrError( sqlite3_context *pCtx, /* Function context */ @@ -89595,7 +90234,7 @@ static void setResultStrOrError( static int invokeValueDestructor( const void *p, /* Value to destroy */ void (*xDel)(void*), /* The destructor */ - sqlite3_context *pCtx /* Set a SQLITE_TOOBIG error if no NULL */ + sqlite3_context *pCtx /* Set a SQLITE_TOOBIG error if not NULL */ ){ assert( xDel!=SQLITE_DYNAMIC ); if( xDel==0 ){ @@ -89605,7 +90244,14 @@ static int invokeValueDestructor( }else{ xDel((void*)p); } +#ifdef SQLITE_ENABLE_API_ARMOR + if( pCtx!=0 ){ + sqlite3_result_error_toobig(pCtx); + } +#else + assert( pCtx!=0 ); sqlite3_result_error_toobig(pCtx); +#endif return SQLITE_TOOBIG; } SQLITE_API void sqlite3_result_blob( @@ -89614,6 +90260,12 @@ SQLITE_API void sqlite3_result_blob( int n, void (*xDel)(void *) ){ +#ifdef SQLITE_ENABLE_API_ARMOR + if( pCtx==0 || n<0 ){ + invokeValueDestructor(z, xDel, pCtx); + return; + } +#endif assert( n>=0 ); assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); setResultStrOrError(pCtx, z, n, 0, xDel); @@ -89624,8 +90276,14 @@ SQLITE_API void sqlite3_result_blob64( sqlite3_uint64 n, void (*xDel)(void *) ){ - assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); assert( xDel!=SQLITE_DYNAMIC ); +#ifdef SQLITE_ENABLE_API_ARMOR + if( pCtx==0 ){ + invokeValueDestructor(z, xDel, 0); + return; + } +#endif + assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); if( n>0x7fffffff ){ (void)invokeValueDestructor(z, xDel, pCtx); }else{ @@ -89633,30 +90291,48 @@ SQLITE_API void sqlite3_result_blob64( } } SQLITE_API void sqlite3_result_double(sqlite3_context *pCtx, double rVal){ +#ifdef SQLITE_ENABLE_API_ARMOR + if( pCtx==0 ) return; +#endif assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); sqlite3VdbeMemSetDouble(pCtx->pOut, rVal); } SQLITE_API void sqlite3_result_error(sqlite3_context *pCtx, const char *z, int n){ +#ifdef SQLITE_ENABLE_API_ARMOR + if( pCtx==0 ) return; +#endif assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); pCtx->isError = SQLITE_ERROR; sqlite3VdbeMemSetStr(pCtx->pOut, z, n, SQLITE_UTF8, SQLITE_TRANSIENT); } #ifndef SQLITE_OMIT_UTF16 SQLITE_API void sqlite3_result_error16(sqlite3_context *pCtx, const void *z, int n){ +#ifdef SQLITE_ENABLE_API_ARMOR + if( pCtx==0 ) return; +#endif assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); pCtx->isError = SQLITE_ERROR; sqlite3VdbeMemSetStr(pCtx->pOut, z, n, SQLITE_UTF16NATIVE, SQLITE_TRANSIENT); } #endif SQLITE_API void sqlite3_result_int(sqlite3_context *pCtx, int iVal){ +#ifdef SQLITE_ENABLE_API_ARMOR + if( pCtx==0 ) return; +#endif assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); sqlite3VdbeMemSetInt64(pCtx->pOut, (i64)iVal); } SQLITE_API void sqlite3_result_int64(sqlite3_context *pCtx, i64 iVal){ +#ifdef SQLITE_ENABLE_API_ARMOR + if( pCtx==0 ) return; +#endif assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); sqlite3VdbeMemSetInt64(pCtx->pOut, iVal); } SQLITE_API void sqlite3_result_null(sqlite3_context *pCtx){ +#ifdef SQLITE_ENABLE_API_ARMOR + if( pCtx==0 ) return; +#endif assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); sqlite3VdbeMemSetNull(pCtx->pOut); } @@ -89666,14 +90342,37 @@ SQLITE_API void sqlite3_result_pointer( const char *zPType, void (*xDestructor)(void*) ){ - Mem *pOut = pCtx->pOut; + Mem *pOut; +#ifdef SQLITE_ENABLE_API_ARMOR + if( pCtx==0 ){ + invokeValueDestructor(pPtr, xDestructor, 0); + return; + } +#endif + pOut = pCtx->pOut; assert( sqlite3_mutex_held(pOut->db->mutex) ); sqlite3VdbeMemRelease(pOut); pOut->flags = MEM_Null; sqlite3VdbeMemSetPointer(pOut, pPtr, zPType, xDestructor); } SQLITE_API void sqlite3_result_subtype(sqlite3_context *pCtx, unsigned int eSubtype){ - Mem *pOut = pCtx->pOut; + Mem *pOut; +#ifdef SQLITE_ENABLE_API_ARMOR + if( pCtx==0 ) return; +#endif +#if defined(SQLITE_STRICT_SUBTYPE) && SQLITE_STRICT_SUBTYPE+0!=0 + if( pCtx->pFunc!=0 + && (pCtx->pFunc->funcFlags & SQLITE_RESULT_SUBTYPE)==0 + ){ + char zErr[200]; + sqlite3_snprintf(sizeof(zErr), zErr, + "misuse of sqlite3_result_subtype() by %s()", + pCtx->pFunc->zName); + sqlite3_result_error(pCtx, zErr, -1); + return; + } +#endif /* SQLITE_STRICT_SUBTYPE */ + pOut = pCtx->pOut; assert( sqlite3_mutex_held(pOut->db->mutex) ); pOut->eSubtype = eSubtype & 0xff; pOut->flags |= MEM_Subtype; @@ -89684,6 +90383,12 @@ SQLITE_API void sqlite3_result_text( int n, void (*xDel)(void *) ){ +#ifdef SQLITE_ENABLE_API_ARMOR + if( pCtx==0 ){ + invokeValueDestructor(z, xDel, 0); + return; + } +#endif assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); setResultStrOrError(pCtx, z, n, SQLITE_UTF8, xDel); } @@ -89694,6 +90399,12 @@ SQLITE_API void sqlite3_result_text64( void (*xDel)(void *), unsigned char enc ){ +#ifdef SQLITE_ENABLE_API_ARMOR + if( pCtx==0 ){ + invokeValueDestructor(z, xDel, 0); + return; + } +#endif assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); assert( xDel!=SQLITE_DYNAMIC ); if( enc!=SQLITE_UTF8 ){ @@ -89737,7 +90448,16 @@ SQLITE_API void sqlite3_result_text16le( } #endif /* SQLITE_OMIT_UTF16 */ SQLITE_API void sqlite3_result_value(sqlite3_context *pCtx, sqlite3_value *pValue){ - Mem *pOut = pCtx->pOut; + Mem *pOut; + +#ifdef SQLITE_ENABLE_API_ARMOR + if( pCtx==0 ) return; + if( pValue==0 ){ + sqlite3_result_null(pCtx); + return; + } +#endif + pOut = pCtx->pOut; assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); sqlite3VdbeMemCopy(pOut, pValue); sqlite3VdbeChangeEncoding(pOut, pCtx->enc); @@ -89749,7 +90469,12 @@ SQLITE_API void sqlite3_result_zeroblob(sqlite3_context *pCtx, int n){ sqlite3_result_zeroblob64(pCtx, n>0 ? n : 0); } SQLITE_API int sqlite3_result_zeroblob64(sqlite3_context *pCtx, u64 n){ - Mem *pOut = pCtx->pOut; + Mem *pOut; + +#ifdef SQLITE_ENABLE_API_ARMOR + if( pCtx==0 ) return SQLITE_MISUSE_BKPT; +#endif + pOut = pCtx->pOut; assert( sqlite3_mutex_held(pOut->db->mutex) ); if( n>(u64)pOut->db->aLimit[SQLITE_LIMIT_LENGTH] ){ sqlite3_result_error_toobig(pCtx); @@ -89763,6 +90488,9 @@ SQLITE_API int sqlite3_result_zeroblob64(sqlite3_context *pCtx, u64 n){ #endif } SQLITE_API void sqlite3_result_error_code(sqlite3_context *pCtx, int errCode){ +#ifdef SQLITE_ENABLE_API_ARMOR + if( pCtx==0 ) return; +#endif pCtx->isError = errCode ? errCode : -1; #ifdef SQLITE_DEBUG if( pCtx->pVdbe ) pCtx->pVdbe->rcApp = errCode; @@ -89775,6 +90503,9 @@ SQLITE_API void sqlite3_result_error_code(sqlite3_context *pCtx, int errCode){ /* Force an SQLITE_TOOBIG error. */ SQLITE_API void sqlite3_result_error_toobig(sqlite3_context *pCtx){ +#ifdef SQLITE_ENABLE_API_ARMOR + if( pCtx==0 ) return; +#endif assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); pCtx->isError = SQLITE_TOOBIG; sqlite3VdbeMemSetStr(pCtx->pOut, "string or blob too big", -1, @@ -89783,6 +90514,9 @@ SQLITE_API void sqlite3_result_error_toobig(sqlite3_context *pCtx){ /* An SQLITE_NOMEM error. */ SQLITE_API void sqlite3_result_error_nomem(sqlite3_context *pCtx){ +#ifdef SQLITE_ENABLE_API_ARMOR + if( pCtx==0 ) return; +#endif assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); sqlite3VdbeMemSetNull(pCtx->pOut); pCtx->isError = SQLITE_NOMEM_BKPT; @@ -90035,6 +90769,9 @@ SQLITE_API int sqlite3_step(sqlite3_stmt *pStmt){ ** pointer to it. */ SQLITE_API void *sqlite3_user_data(sqlite3_context *p){ +#ifdef SQLITE_ENABLE_API_ARMOR + if( p==0 ) return 0; +#endif assert( p && p->pFunc ); return p->pFunc->pUserData; } @@ -90050,7 +90787,11 @@ SQLITE_API void *sqlite3_user_data(sqlite3_context *p){ ** application defined function. */ SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context *p){ +#ifdef SQLITE_ENABLE_API_ARMOR + if( p==0 ) return 0; +#else assert( p && p->pOut ); +#endif return p->pOut->db; } @@ -90069,7 +90810,11 @@ SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context *p){ ** value, as a signal to the xUpdate routine that the column is unchanged. */ SQLITE_API int sqlite3_vtab_nochange(sqlite3_context *p){ +#ifdef SQLITE_ENABLE_API_ARMOR + if( p==0 ) return 0; +#else assert( p ); +#endif return sqlite3_value_nochange(p->pOut); } @@ -90097,7 +90842,7 @@ static int valueFromValueList( ValueList *pRhs; *ppOut = 0; - if( pVal==0 ) return SQLITE_MISUSE; + if( pVal==0 ) return SQLITE_MISUSE_BKPT; if( (pVal->flags & MEM_Dyn)==0 || pVal->xDel!=sqlite3VdbeValueListFree ){ return SQLITE_ERROR; }else{ @@ -90228,6 +90973,9 @@ SQLITE_API void *sqlite3_aggregate_context(sqlite3_context *p, int nByte){ SQLITE_API void *sqlite3_get_auxdata(sqlite3_context *pCtx, int iArg){ AuxData *pAuxData; +#ifdef SQLITE_ENABLE_API_ARMOR + if( pCtx==0 ) return 0; +#endif assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); #if SQLITE_ENABLE_STAT4 if( pCtx->pVdbe==0 ) return 0; @@ -90260,8 +91008,12 @@ SQLITE_API void sqlite3_set_auxdata( void (*xDelete)(void*) ){ AuxData *pAuxData; - Vdbe *pVdbe = pCtx->pVdbe; + Vdbe *pVdbe; +#ifdef SQLITE_ENABLE_API_ARMOR + if( pCtx==0 ) return; +#endif + pVdbe= pCtx->pVdbe; assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); #ifdef SQLITE_ENABLE_STAT4 if( pVdbe==0 ) goto failed; @@ -90698,7 +91450,7 @@ static int vdbeUnbind(Vdbe *p, unsigned int i){ } sqlite3_mutex_enter(p->db->mutex); if( p->eVdbeState!=VDBE_READY_STATE ){ - sqlite3Error(p->db, SQLITE_MISUSE); + sqlite3Error(p->db, SQLITE_MISUSE_BKPT); sqlite3_mutex_leave(p->db->mutex); sqlite3_log(SQLITE_MISUSE, "bind on a busy prepared statement: [%s]", p->zSql); @@ -90927,6 +91679,9 @@ SQLITE_API int sqlite3_bind_zeroblob(sqlite3_stmt *pStmt, int i, int n){ SQLITE_API int sqlite3_bind_zeroblob64(sqlite3_stmt *pStmt, int i, sqlite3_uint64 n){ int rc; Vdbe *p = (Vdbe *)pStmt; +#ifdef SQLITE_ENABLE_API_ARMOR + if( p==0 ) return SQLITE_MISUSE_BKPT; +#endif sqlite3_mutex_enter(p->db->mutex); if( n>(u64)p->db->aLimit[SQLITE_LIMIT_LENGTH] ){ rc = SQLITE_TOOBIG; @@ -91053,6 +91808,9 @@ SQLITE_API int sqlite3_stmt_isexplain(sqlite3_stmt *pStmt){ SQLITE_API int sqlite3_stmt_explain(sqlite3_stmt *pStmt, int eMode){ Vdbe *v = (Vdbe*)pStmt; int rc; +#ifdef SQLITE_ENABLE_API_ARMOR + if( pStmt==0 ) return SQLITE_MISUSE_BKPT; +#endif sqlite3_mutex_enter(v->db->mutex); if( ((int)v->explain)==eMode ){ rc = SQLITE_OK; @@ -91219,10 +91977,16 @@ static UnpackedRecord *vdbeUnpackRecord( ** a field of the row currently being updated or deleted. */ SQLITE_API int sqlite3_preupdate_old(sqlite3 *db, int iIdx, sqlite3_value **ppValue){ - PreUpdate *p = db->pPreUpdate; + PreUpdate *p; Mem *pMem; int rc = SQLITE_OK; +#ifdef SQLITE_ENABLE_API_ARMOR + if( db==0 || ppValue==0 ){ + return SQLITE_MISUSE_BKPT; + } +#endif + p = db->pPreUpdate; /* Test that this call is being made from within an SQLITE_DELETE or ** SQLITE_UPDATE pre-update callback, and that iIdx is within range. */ if( !p || p->op==SQLITE_INSERT ){ @@ -91283,7 +92047,12 @@ SQLITE_API int sqlite3_preupdate_old(sqlite3 *db, int iIdx, sqlite3_value **ppVa ** the number of columns in the row being updated, deleted or inserted. */ SQLITE_API int sqlite3_preupdate_count(sqlite3 *db){ - PreUpdate *p = db->pPreUpdate; + PreUpdate *p; +#ifdef SQLITE_ENABLE_API_ARMOR + p = db!=0 ? db->pPreUpdate : 0; +#else + p = db->pPreUpdate; +#endif return (p ? p->keyinfo.nKeyField : 0); } #endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ @@ -91301,7 +92070,12 @@ SQLITE_API int sqlite3_preupdate_count(sqlite3 *db){ ** or SET DEFAULT action is considered a trigger. */ SQLITE_API int sqlite3_preupdate_depth(sqlite3 *db){ - PreUpdate *p = db->pPreUpdate; + PreUpdate *p; +#ifdef SQLITE_ENABLE_API_ARMOR + p = db!=0 ? db->pPreUpdate : 0; +#else + p = db->pPreUpdate; +#endif return (p ? p->v->nFrame : 0); } #endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ @@ -91312,7 +92086,12 @@ SQLITE_API int sqlite3_preupdate_depth(sqlite3 *db){ ** only. */ SQLITE_API int sqlite3_preupdate_blobwrite(sqlite3 *db){ - PreUpdate *p = db->pPreUpdate; + PreUpdate *p; +#ifdef SQLITE_ENABLE_API_ARMOR + p = db!=0 ? db->pPreUpdate : 0; +#else + p = db->pPreUpdate; +#endif return (p ? p->iBlobWrite : -1); } #endif @@ -91323,10 +92102,16 @@ SQLITE_API int sqlite3_preupdate_blobwrite(sqlite3 *db){ ** a field of the row currently being updated or inserted. */ SQLITE_API int sqlite3_preupdate_new(sqlite3 *db, int iIdx, sqlite3_value **ppValue){ - PreUpdate *p = db->pPreUpdate; + PreUpdate *p; int rc = SQLITE_OK; Mem *pMem; +#ifdef SQLITE_ENABLE_API_ARMOR + if( db==0 || ppValue==0 ){ + return SQLITE_MISUSE_BKPT; + } +#endif + p = db->pPreUpdate; if( !p || p->op==SQLITE_DELETE ){ rc = SQLITE_MISUSE_BKPT; goto preupdate_new_out; @@ -91405,11 +92190,20 @@ SQLITE_API int sqlite3_stmt_scanstatus_v2( void *pOut /* OUT: Write the answer here */ ){ Vdbe *p = (Vdbe*)pStmt; - VdbeOp *aOp = p->aOp; - int nOp = p->nOp; + VdbeOp *aOp; + int nOp; ScanStatus *pScan = 0; int idx; +#ifdef SQLITE_ENABLE_API_ARMOR + if( p==0 || pOut==0 + || iScanStatusOpSQLITE_SCANSTAT_NCYCLE ){ + return 1; + } +#endif + aOp = p->aOp; + nOp = p->nOp; if( p->pFrame ){ VdbeFrame *pFrame; for(pFrame=p->pFrame; pFrame->pParent; pFrame=pFrame->pParent); @@ -91556,7 +92350,7 @@ SQLITE_API int sqlite3_stmt_scanstatus( SQLITE_API void sqlite3_stmt_scanstatus_reset(sqlite3_stmt *pStmt){ Vdbe *p = (Vdbe*)pStmt; int ii; - for(ii=0; iinOp; ii++){ + for(ii=0; p!=0 && iinOp; ii++){ Op *pOp = &p->aOp[ii]; pOp->nExec = 0; pOp->nCycle = 0; @@ -91895,11 +92689,12 @@ SQLITE_API int sqlite3_found_count = 0; ** sqlite3CantopenError(lineno) */ static void test_trace_breakpoint(int pc, Op *pOp, Vdbe *v){ - static int n = 0; + static u64 n = 0; (void)pc; (void)pOp; (void)v; n++; + if( n==LARGEST_UINT64 ) abort(); /* So that n is used, preventing a warning */ } #endif @@ -92525,11 +93320,11 @@ static SQLITE_NOINLINE int vdbeColumnFromOverflow( sqlite3RCStrRef(pBuf); if( t&1 ){ rc = sqlite3VdbeMemSetStr(pDest, pBuf, len, encoding, - (void(*)(void*))sqlite3RCStrUnref); + sqlite3RCStrUnref); pDest->flags |= MEM_Term; }else{ rc = sqlite3VdbeMemSetStr(pDest, pBuf, len, 0, - (void(*)(void*))sqlite3RCStrUnref); + sqlite3RCStrUnref); } }else{ rc = sqlite3VdbeMemFromBtree(pC->uc.pCursor, iOffset, len, pDest); @@ -93796,7 +94591,7 @@ case OP_AddImm: { /* in1 */ pIn1 = &aMem[pOp->p1]; memAboutToChange(p, pIn1); sqlite3VdbeMemIntegerify(pIn1); - pIn1->u.i += pOp->p2; + *(u64*)&pIn1->u.i += (u64)pOp->p2; break; } @@ -95404,7 +96199,6 @@ case OP_MakeRecord: { /* NULL value. No change in zPayload */ }else{ u64 v; - u32 i; if( serial_type==7 ){ assert( sizeof(v)==sizeof(pRec->u.r) ); memcpy(&v, &pRec->u.r, sizeof(v)); @@ -95412,12 +96206,17 @@ case OP_MakeRecord: { }else{ v = pRec->u.i; } - len = i = sqlite3SmallTypeSizes[serial_type]; - assert( i>0 ); - while( 1 /*exit-by-break*/ ){ - zPayload[--i] = (u8)(v&0xFF); - if( i==0 ) break; - v >>= 8; + len = sqlite3SmallTypeSizes[serial_type]; + assert( len>=1 && len<=8 && len!=5 && len!=7 ); + switch( len ){ + default: zPayload[7] = (u8)(v&0xff); v >>= 8; + zPayload[6] = (u8)(v&0xff); v >>= 8; + case 6: zPayload[5] = (u8)(v&0xff); v >>= 8; + zPayload[4] = (u8)(v&0xff); v >>= 8; + case 4: zPayload[3] = (u8)(v&0xff); v >>= 8; + case 3: zPayload[2] = (u8)(v&0xff); v >>= 8; + case 2: zPayload[1] = (u8)(v&0xff); v >>= 8; + case 1: zPayload[0] = (u8)(v&0xff); } zPayload += len; } @@ -97534,8 +98333,13 @@ case OP_RowCell: { ** the "primary" delete. The others are all on OPFLAG_FORDELETE ** cursors or else are marked with the AUXDELETE flag. ** -** If the OPFLAG_NCHANGE flag of P2 (NB: P2 not P5) is set, then the row -** change count is incremented (otherwise not). +** If the OPFLAG_NCHANGE (0x01) flag of P2 (NB: P2 not P5) is set, then +** the row change count is incremented (otherwise not). +** +** If the OPFLAG_ISNOOP (0x40) flag of P2 (not P5!) is set, then the +** pre-update-hook for deletes is run, but the btree is otherwise unchanged. +** This happens when the OP_Delete is to be shortly followed by an OP_Insert +** with the same key, causing the btree entry to be overwritten. ** ** P1 must not be pseudo-table. It has to be a real table with ** multiple rows. @@ -98660,13 +99464,41 @@ case OP_CreateBtree: { /* out2 */ /* Opcode: SqlExec * * * P4 * ** ** Run the SQL statement or statements specified in the P4 string. +** Disable Auth and Trace callbacks while those statements are running if +** P1 is true. */ case OP_SqlExec: { + char *zErr; +#ifndef SQLITE_OMIT_AUTHORIZATION + sqlite3_xauth xAuth; +#endif + u8 mTrace; + sqlite3VdbeIncrWriteCounter(p, 0); db->nSqlExec++; - rc = sqlite3_exec(db, pOp->p4.z, 0, 0, 0); + zErr = 0; +#ifndef SQLITE_OMIT_AUTHORIZATION + xAuth = db->xAuth; +#endif + mTrace = db->mTrace; + if( pOp->p1 ){ +#ifndef SQLITE_OMIT_AUTHORIZATION + db->xAuth = 0; +#endif + db->mTrace = 0; + } + rc = sqlite3_exec(db, pOp->p4.z, 0, 0, &zErr); db->nSqlExec--; - if( rc ) goto abort_due_to_error; +#ifndef SQLITE_OMIT_AUTHORIZATION + db->xAuth = xAuth; +#endif + db->mTrace = mTrace; + if( zErr || rc ){ + sqlite3VdbeError(p, "%s", zErr); + sqlite3_free(zErr); + if( rc==SQLITE_NOMEM ) goto no_mem; + goto abort_due_to_error; + } break; } @@ -99887,6 +100719,52 @@ case OP_VOpen: { /* ncycle */ } #endif /* SQLITE_OMIT_VIRTUALTABLE */ +#ifndef SQLITE_OMIT_VIRTUALTABLE +/* Opcode: VCheck P1 P2 P3 P4 * +** +** P4 is a pointer to a Table object that is a virtual table in schema P1 +** that supports the xIntegrity() method. This opcode runs the xIntegrity() +** method for that virtual table, using P3 as the integer argument. If +** an error is reported back, the table name is prepended to the error +** message and that message is stored in P2. If no errors are seen, +** register P2 is set to NULL. +*/ +case OP_VCheck: { /* out2 */ + Table *pTab; + sqlite3_vtab *pVtab; + const sqlite3_module *pModule; + char *zErr = 0; + + pOut = &aMem[pOp->p2]; + sqlite3VdbeMemSetNull(pOut); /* Innocent until proven guilty */ + assert( pOp->p4type==P4_TABLEREF ); + pTab = pOp->p4.pTab; + assert( pTab!=0 ); + assert( pTab->nTabRef>0 ); + assert( IsVirtual(pTab) ); + if( pTab->u.vtab.p==0 ) break; + pVtab = pTab->u.vtab.p->pVtab; + assert( pVtab!=0 ); + pModule = pVtab->pModule; + assert( pModule!=0 ); + assert( pModule->iVersion>=4 ); + assert( pModule->xIntegrity!=0 ); + sqlite3VtabLock(pTab->u.vtab.p); + assert( pOp->p1>=0 && pOp->p1nDb ); + rc = pModule->xIntegrity(pVtab, db->aDb[pOp->p1].zDbSName, pTab->zName, + pOp->p3, &zErr); + sqlite3VtabUnlock(pTab->u.vtab.p); + if( rc ){ + sqlite3_free(zErr); + goto abort_due_to_error; + } + if( zErr ){ + sqlite3VdbeMemSetStr(pOut, zErr, -1, SQLITE_UTF8, sqlite3_free); + } + break; +} +#endif /* SQLITE_OMIT_VIRTUALTABLE */ + #ifndef SQLITE_OMIT_VIRTUALTABLE /* Opcode: VInitIn P1 P2 P3 * * ** Synopsis: r[P2]=ValueList(P1,P3) @@ -100000,6 +100878,7 @@ case OP_VColumn: { /* ncycle */ const sqlite3_module *pModule; Mem *pDest; sqlite3_context sContext; + FuncDef nullFunc; VdbeCursor *pCur = p->apCsr[pOp->p1]; assert( pCur!=0 ); @@ -100017,6 +100896,9 @@ case OP_VColumn: { /* ncycle */ memset(&sContext, 0, sizeof(sContext)); sContext.pOut = pDest; sContext.enc = encoding; + nullFunc.pUserData = 0; + nullFunc.funcFlags = SQLITE_RESULT_SUBTYPE; + sContext.pFunc = &nullFunc; assert( pOp->p5==OPFLAG_NOCHNG || pOp->p5==0 ); if( pOp->p5 & OPFLAG_NOCHNG ){ sqlite3VdbeMemSetNull(pDest); @@ -100349,6 +101231,42 @@ case OP_ClrSubtype: { /* in1 */ break; } +/* Opcode: GetSubtype P1 P2 * * * +** Synopsis: r[P2] = r[P1].subtype +** +** Extract the subtype value from register P1 and write that subtype +** into register P2. If P1 has no subtype, then P1 gets a NULL. +*/ +case OP_GetSubtype: { /* in1 out2 */ + pIn1 = &aMem[pOp->p1]; + pOut = &aMem[pOp->p2]; + if( pIn1->flags & MEM_Subtype ){ + sqlite3VdbeMemSetInt64(pOut, pIn1->eSubtype); + }else{ + sqlite3VdbeMemSetNull(pOut); + } + break; +} + +/* Opcode: SetSubtype P1 P2 * * * +** Synopsis: r[P2].subtype = r[P1] +** +** Set the subtype value of register P2 to the integer from register P1. +** If P1 is NULL, clear the subtype from p2. +*/ +case OP_SetSubtype: { /* in1 out2 */ + pIn1 = &aMem[pOp->p1]; + pOut = &aMem[pOp->p2]; + if( pIn1->flags & MEM_Null ){ + pOut->flags &= ~MEM_Subtype; + }else{ + assert( pIn1->flags & MEM_Int ); + pOut->flags |= MEM_Subtype; + pOut->eSubtype = (u8)(pIn1->u.i & 0xff); + } + break; +} + /* Opcode: FilterAdd P1 * P3 P4 * ** Synopsis: filter(P1) += key(P3@P4) ** @@ -100833,8 +101751,7 @@ static int blobSeekToRow(Incrblob *p, sqlite3_int64 iRow, char **pzErr){ /* Set the value of register r[1] in the SQL statement to integer iRow. ** This is done directly as a performance optimization */ - v->aMem[1].flags = MEM_Int; - v->aMem[1].u.i = iRow; + sqlite3VdbeMemSetInt64(&v->aMem[1], iRow); /* If the statement has been run before (and is paused at the OP_ResultRow) ** then back it up to the point where it does the OP_NotExists. This could @@ -100917,7 +101834,7 @@ SQLITE_API int sqlite3_blob_open( #endif *ppBlob = 0; #ifdef SQLITE_ENABLE_API_ARMOR - if( !sqlite3SafetyCheckOk(db) || zTable==0 ){ + if( !sqlite3SafetyCheckOk(db) || zTable==0 || zColumn==0 ){ return SQLITE_MISUSE_BKPT; } #endif @@ -101479,7 +102396,7 @@ struct SorterFile { struct SorterList { SorterRecord *pList; /* Linked list of records */ u8 *aMemory; /* If non-NULL, bulk memory to hold pList */ - int szPMA; /* Size of pList as PMA in bytes */ + i64 szPMA; /* Size of pList as PMA in bytes */ }; /* @@ -101588,10 +102505,10 @@ typedef int (*SorterCompare)(SortSubtask*,int*,const void*,int,const void*,int); struct SortSubtask { SQLiteThread *pThread; /* Background thread, if any */ int bDone; /* Set if thread is finished but not joined */ + int nPMA; /* Number of PMAs currently in file */ VdbeSorter *pSorter; /* Sorter that owns this sub-task */ UnpackedRecord *pUnpacked; /* Space to unpack a record */ SorterList list; /* List for thread to write to a PMA */ - int nPMA; /* Number of PMAs currently in file */ SorterCompare xCompare; /* Compare function to use */ SorterFile file; /* Temp file for level-0 PMAs */ SorterFile file2; /* Space for other PMAs */ @@ -103065,8 +103982,8 @@ SQLITE_PRIVATE int sqlite3VdbeSorterWrite( int rc = SQLITE_OK; /* Return Code */ SorterRecord *pNew; /* New list element */ int bFlush; /* True to flush contents of memory to PMA */ - int nReq; /* Bytes of memory required */ - int nPMA; /* Bytes of PMA space required */ + i64 nReq; /* Bytes of memory required */ + i64 nPMA; /* Bytes of PMA space required */ int t; /* serial type of first record field */ assert( pCsr->eCurType==CURTYPE_SORTER ); @@ -104490,7 +105407,8 @@ static sqlite3_module bytecodevtabModule = { /* xSavepoint */ 0, /* xRelease */ 0, /* xRollbackTo */ 0, - /* xShadowName */ 0 + /* xShadowName */ 0, + /* xIntegrity */ 0 }; @@ -105319,21 +106237,36 @@ static void resolveAlias( } /* -** Subqueries stores the original database, table and column names for their -** result sets in ExprList.a[].zSpan, in the form "DATABASE.TABLE.COLUMN". -** Check to see if the zSpan given to this routine matches the zDb, zTab, -** and zCol. If any of zDb, zTab, and zCol are NULL then those fields will -** match anything. +** Subqueries store the original database, table and column names for their +** result sets in ExprList.a[].zSpan, in the form "DATABASE.TABLE.COLUMN", +** and mark the expression-list item by setting ExprList.a[].fg.eEName +** to ENAME_TAB. +** +** Check to see if the zSpan/eEName of the expression-list item passed to this +** routine matches the zDb, zTab, and zCol. If any of zDb, zTab, and zCol are +** NULL then those fields will match anything. Return true if there is a match, +** or false otherwise. +** +** SF_NestedFrom subqueries also store an entry for the implicit rowid (or +** _rowid_, or oid) column by setting ExprList.a[].fg.eEName to ENAME_ROWID, +** and setting zSpan to "DATABASE.TABLE.". This type of pItem +** argument matches if zCol is a rowid alias. If it is not NULL, (*pbRowid) +** is set to 1 if there is this kind of match. */ SQLITE_PRIVATE int sqlite3MatchEName( const struct ExprList_item *pItem, const char *zCol, const char *zTab, - const char *zDb + const char *zDb, + int *pbRowid ){ int n; const char *zSpan; - if( pItem->fg.eEName!=ENAME_TAB ) return 0; + int eEName = pItem->fg.eEName; + if( eEName!=ENAME_TAB && (eEName!=ENAME_ROWID || NEVER(pbRowid==0)) ){ + return 0; + } + assert( pbRowid==0 || *pbRowid==0 ); zSpan = pItem->zEName; for(n=0; ALWAYS(zSpan[n]) && zSpan[n]!='.'; n++){} if( zDb && (sqlite3StrNICmp(zSpan, zDb, n)!=0 || zDb[n]!=0) ){ @@ -105345,9 +106278,11 @@ SQLITE_PRIVATE int sqlite3MatchEName( return 0; } zSpan += n+1; - if( zCol && sqlite3StrICmp(zSpan, zCol)!=0 ){ - return 0; + if( zCol ){ + if( eEName==ENAME_TAB && sqlite3StrICmp(zSpan, zCol)!=0 ) return 0; + if( eEName==ENAME_ROWID && sqlite3IsRowid(zCol)==0 ) return 0; } + if( eEName==ENAME_ROWID ) *pbRowid = 1; return 1; } @@ -105380,6 +106315,7 @@ SQLITE_PRIVATE Bitmask sqlite3ExprColUsed(Expr *pExpr){ assert( ExprUseYTab(pExpr) ); pExTab = pExpr->y.pTab; assert( pExTab!=0 ); + assert( n < pExTab->nCol ); if( (pExTab->tabFlags & TF_HasGenerated)!=0 && (pExTab->aCol[n].colFlags & COLFLAG_GENERATED)!=0 ){ @@ -105480,7 +106416,7 @@ static int lookupName( ){ int i, j; /* Loop counters */ int cnt = 0; /* Number of matching column names */ - int cntTab = 0; /* Number of matching table names */ + int cntTab = 0; /* Number of potential "rowid" matches */ int nSubquery = 0; /* How many levels of subquery */ sqlite3 *db = pParse->db; /* The database connection */ SrcItem *pItem; /* Use for looping over pSrcList items */ @@ -105557,39 +106493,49 @@ static int lookupName( assert( pEList!=0 ); assert( pEList->nExpr==pTab->nCol ); for(j=0; jnExpr; j++){ - if( !sqlite3MatchEName(&pEList->a[j], zCol, zTab, zDb) ){ + int bRowid = 0; /* True if possible rowid match */ + if( !sqlite3MatchEName(&pEList->a[j], zCol, zTab, zDb, &bRowid) ){ continue; } - if( cnt>0 ){ - if( pItem->fg.isUsing==0 - || sqlite3IdListIndex(pItem->u3.pUsing, zCol)<0 - ){ - /* Two or more tables have the same column name which is - ** not joined by USING. This is an error. Signal as much - ** by clearing pFJMatch and letting cnt go above 1. */ - sqlite3ExprListDelete(db, pFJMatch); - pFJMatch = 0; - }else - if( (pItem->fg.jointype & JT_RIGHT)==0 ){ - /* An INNER or LEFT JOIN. Use the left-most table */ - continue; - }else - if( (pItem->fg.jointype & JT_LEFT)==0 ){ - /* A RIGHT JOIN. Use the right-most table */ - cnt = 0; - sqlite3ExprListDelete(db, pFJMatch); - pFJMatch = 0; - }else{ - /* For a FULL JOIN, we must construct a coalesce() func */ - extendFJMatch(pParse, &pFJMatch, pMatch, pExpr->iColumn); + if( bRowid==0 ){ + if( cnt>0 ){ + if( pItem->fg.isUsing==0 + || sqlite3IdListIndex(pItem->u3.pUsing, zCol)<0 + ){ + /* Two or more tables have the same column name which is + ** not joined by USING. This is an error. Signal as much + ** by clearing pFJMatch and letting cnt go above 1. */ + sqlite3ExprListDelete(db, pFJMatch); + pFJMatch = 0; + }else + if( (pItem->fg.jointype & JT_RIGHT)==0 ){ + /* An INNER or LEFT JOIN. Use the left-most table */ + continue; + }else + if( (pItem->fg.jointype & JT_LEFT)==0 ){ + /* A RIGHT JOIN. Use the right-most table */ + cnt = 0; + sqlite3ExprListDelete(db, pFJMatch); + pFJMatch = 0; + }else{ + /* For a FULL JOIN, we must construct a coalesce() func */ + extendFJMatch(pParse, &pFJMatch, pMatch, pExpr->iColumn); + } } + cnt++; + hit = 1; + }else if( cnt>0 ){ + /* This is a potential rowid match, but there has already been + ** a real match found. So this can be ignored. */ + continue; } - cnt++; - cntTab = 2; + cntTab++; pMatch = pItem; pExpr->iColumn = j; pEList->a[j].fg.bUsed = 1; - hit = 1; + + /* rowid cannot be part of a USING clause - assert() this. */ + assert( bRowid==0 || pEList->a[j].fg.bUsingTerm==0 ); if( pEList->a[j].fg.bUsingTerm ) break; } if( hit || zTab==0 ) continue; @@ -105784,10 +106730,10 @@ static int lookupName( && pMatch && (pNC->ncFlags & (NC_IdxExpr|NC_GenCol))==0 && sqlite3IsRowid(zCol) - && ALWAYS(VisibleRowid(pMatch->pTab)) + && ALWAYS(VisibleRowid(pMatch->pTab) || pMatch->fg.isNestedFrom) ){ cnt = 1; - pExpr->iColumn = -1; + if( pMatch->fg.isNestedFrom==0 ) pExpr->iColumn = -1; pExpr->affExpr = SQLITE_AFF_INTEGER; } @@ -105946,6 +106892,7 @@ static int lookupName( sqlite3RecordErrorOffsetOfExpr(pParse->db, pExpr); pParse->checkSchema = 1; pTopNC->nNcErr++; + eNewExprOp = TK_NULL; } assert( pFJMatch==0 ); @@ -105972,7 +106919,7 @@ static int lookupName( ** If a generated column is referenced, set bits for every column ** of the table. */ - if( pExpr->iColumn>=0 && pMatch!=0 ){ + if( pExpr->iColumn>=0 && cnt==1 && pMatch!=0 ){ pMatch->colUsed |= sqlite3ExprColUsed(pExpr); } @@ -106240,6 +107187,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ Window *pWin = (IsWindowFunc(pExpr) ? pExpr->y.pWin : 0); #endif assert( !ExprHasProperty(pExpr, EP_xIsSelect|EP_IntValue) ); + assert( pExpr->pLeft==0 || pExpr->pLeft->op==TK_ORDER ); zId = pExpr->u.zToken; pDef = sqlite3FindFunction(pParse->db, zId, n, enc, 0); if( pDef==0 ){ @@ -106381,6 +107329,10 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ pNC->nNcErr++; } #endif + else if( is_agg==0 && pExpr->pLeft ){ + sqlite3ExprOrderByAggregateError(pParse, pExpr); + pNC->nNcErr++; + } if( is_agg ){ /* Window functions may not be arguments of aggregate functions. ** Or arguments of other window functions. But aggregate functions @@ -106399,6 +107351,11 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ #endif sqlite3WalkExprList(pWalker, pList); if( is_agg ){ + if( pExpr->pLeft ){ + assert( pExpr->pLeft->op==TK_ORDER ); + assert( ExprUseXList(pExpr->pLeft) ); + sqlite3WalkExprList(pWalker, pExpr->pLeft->x.pList); + } #ifndef SQLITE_OMIT_WINDOWFUNC if( pWin ){ Select *pSel = pNC->pWinSelect; @@ -106427,11 +107384,12 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ while( pNC2 && sqlite3ReferencesSrcList(pParse, pExpr, pNC2->pSrcList)==0 ){ - pExpr->op2++; + pExpr->op2 += (1 + pNC2->nNestedSelect); pNC2 = pNC2->pNext; } assert( pDef!=0 || IN_RENAME_OBJECT ); if( pNC2 && pDef ){ + pExpr->op2 += pNC2->nNestedSelect; assert( SQLITE_FUNC_MINMAX==NC_MinMaxAgg ); assert( SQLITE_FUNC_ANYORDER==NC_OrderAgg ); testcase( (pDef->funcFlags & SQLITE_FUNC_MINMAX)!=0 ); @@ -106962,10 +107920,8 @@ static int resolveSelectStep(Walker *pWalker, Select *p){ while( p ){ assert( (p->selFlags & SF_Expanded)!=0 ); assert( (p->selFlags & SF_Resolved)==0 ); - assert( db->suppressErr==0 ); /* SF_Resolved not set if errors suppressed */ p->selFlags |= SF_Resolved; - /* Resolve the expressions in the LIMIT and OFFSET clauses. These ** are not allowed to refer to any names, so pass an empty NameContext. */ @@ -106992,6 +107948,7 @@ static int resolveSelectStep(Walker *pWalker, Select *p){ /* Recursively resolve names in all subqueries in the FROM clause */ + if( pOuterNC ) pOuterNC->nNestedSelect++; for(i=0; ipSrc->nSrc; i++){ SrcItem *pItem = &p->pSrc->a[i]; if( pItem->pSelect && (pItem->pSelect->selFlags & SF_Resolved)==0 ){ @@ -107016,6 +107973,9 @@ static int resolveSelectStep(Walker *pWalker, Select *p){ } } } + if( pOuterNC && ALWAYS(pOuterNC->nNestedSelect>0) ){ + pOuterNC->nNestedSelect--; + } /* Set up the local name-context to pass to sqlite3ResolveExprNames() to ** resolve the result-set expression list. @@ -107971,6 +108931,7 @@ SQLITE_PRIVATE Expr *sqlite3ExprForVectorField( */ pRet = sqlite3PExpr(pParse, TK_SELECT_COLUMN, 0, 0); if( pRet ){ + ExprSetProperty(pRet, EP_FullSize); pRet->iTable = nField; pRet->iColumn = iField; pRet->pLeft = pVector; @@ -108561,6 +109522,67 @@ SQLITE_PRIVATE Expr *sqlite3ExprFunction( return pNew; } +/* +** Report an error when attempting to use an ORDER BY clause within +** the arguments of a non-aggregate function. +*/ +SQLITE_PRIVATE void sqlite3ExprOrderByAggregateError(Parse *pParse, Expr *p){ + sqlite3ErrorMsg(pParse, + "ORDER BY may not be used with non-aggregate %#T()", p + ); +} + +/* +** Attach an ORDER BY clause to a function call. +** +** functionname( arguments ORDER BY sortlist ) +** \_____________________/ \______/ +** pExpr pOrderBy +** +** The ORDER BY clause is inserted into a new Expr node of type TK_ORDER +** and added to the Expr.pLeft field of the parent TK_FUNCTION node. +*/ +SQLITE_PRIVATE void sqlite3ExprAddFunctionOrderBy( + Parse *pParse, /* Parsing context */ + Expr *pExpr, /* The function call to which ORDER BY is to be added */ + ExprList *pOrderBy /* The ORDER BY clause to add */ +){ + Expr *pOB; + sqlite3 *db = pParse->db; + if( NEVER(pOrderBy==0) ){ + assert( db->mallocFailed ); + return; + } + if( pExpr==0 ){ + assert( db->mallocFailed ); + sqlite3ExprListDelete(db, pOrderBy); + return; + } + assert( pExpr->op==TK_FUNCTION ); + assert( pExpr->pLeft==0 ); + assert( ExprUseXList(pExpr) ); + if( pExpr->x.pList==0 || NEVER(pExpr->x.pList->nExpr==0) ){ + /* Ignore ORDER BY on zero-argument aggregates */ + sqlite3ParserAddCleanup(pParse, sqlite3ExprListDeleteGeneric, pOrderBy); + return; + } + if( IsWindowFunc(pExpr) ){ + sqlite3ExprOrderByAggregateError(pParse, pExpr); + sqlite3ExprListDelete(db, pOrderBy); + return; + } + + pOB = sqlite3ExprAlloc(db, TK_ORDER, 0, 0); + if( pOB==0 ){ + sqlite3ExprListDelete(db, pOrderBy); + return; + } + pOB->x.pList = pOrderBy; + assert( ExprUseXList(pOB) ); + pExpr->pLeft = pOB; + ExprSetProperty(pOB, EP_FullSize); +} + /* ** Check to see if a function is usable according to current access ** rules: @@ -108722,6 +109744,9 @@ static SQLITE_NOINLINE void sqlite3ExprDeleteNN(sqlite3 *db, Expr *p){ SQLITE_PRIVATE void sqlite3ExprDelete(sqlite3 *db, Expr *p){ if( p ) sqlite3ExprDeleteNN(db, p); } +SQLITE_PRIVATE void sqlite3ExprDeleteGeneric(sqlite3 *db, void *p){ + if( ALWAYS(p) ) sqlite3ExprDeleteNN(db, (Expr*)p); +} /* ** Clear both elements of an OnOrUsing object @@ -108747,9 +109772,7 @@ SQLITE_PRIVATE void sqlite3ClearOnOrUsing(sqlite3 *db, OnOrUsing *p){ ** pExpr to the pParse->pConstExpr list with a register number of 0. */ SQLITE_PRIVATE void sqlite3ExprDeferredDelete(Parse *pParse, Expr *pExpr){ - sqlite3ParserAddCleanup(pParse, - (void(*)(sqlite3*,void*))sqlite3ExprDelete, - pExpr); + sqlite3ParserAddCleanup(pParse, sqlite3ExprDeleteGeneric, pExpr); } /* Invoke sqlite3RenameExprUnmap() and sqlite3ExprDelete() on the @@ -108814,11 +109837,7 @@ static int dupedExprStructSize(const Expr *p, int flags){ assert( flags==EXPRDUP_REDUCE || flags==0 ); /* Only one flag value allowed */ assert( EXPR_FULLSIZE<=0xfff ); assert( (0xfff & (EP_Reduced|EP_TokenOnly))==0 ); - if( 0==flags || p->op==TK_SELECT_COLUMN -#ifndef SQLITE_OMIT_WINDOWFUNC - || ExprHasProperty(p, EP_WinFunc) -#endif - ){ + if( 0==flags || ExprHasProperty(p, EP_FullSize) ){ nSize = EXPR_FULLSIZE; }else{ assert( !ExprHasProperty(p, EP_TokenOnly|EP_Reduced) ); @@ -108849,56 +109868,93 @@ static int dupedExprNodeSize(const Expr *p, int flags){ /* ** Return the number of bytes required to create a duplicate of the -** expression passed as the first argument. The second argument is a -** mask containing EXPRDUP_XXX flags. +** expression passed as the first argument. ** ** The value returned includes space to create a copy of the Expr struct ** itself and the buffer referred to by Expr.u.zToken, if any. ** -** If the EXPRDUP_REDUCE flag is set, then the return value includes -** space to duplicate all Expr nodes in the tree formed by Expr.pLeft -** and Expr.pRight variables (but not for any structures pointed to or -** descended from the Expr.x.pList or Expr.x.pSelect variables). +** The return value includes space to duplicate all Expr nodes in the +** tree formed by Expr.pLeft and Expr.pRight, but not any other +** substructure such as Expr.x.pList, Expr.x.pSelect, and Expr.y.pWin. */ -static int dupedExprSize(const Expr *p, int flags){ - int nByte = 0; - if( p ){ - nByte = dupedExprNodeSize(p, flags); - if( flags&EXPRDUP_REDUCE ){ - nByte += dupedExprSize(p->pLeft, flags) + dupedExprSize(p->pRight, flags); - } - } +static int dupedExprSize(const Expr *p){ + int nByte; + assert( p!=0 ); + nByte = dupedExprNodeSize(p, EXPRDUP_REDUCE); + if( p->pLeft ) nByte += dupedExprSize(p->pLeft); + if( p->pRight ) nByte += dupedExprSize(p->pRight); + assert( nByte==ROUND8(nByte) ); return nByte; } /* -** This function is similar to sqlite3ExprDup(), except that if pzBuffer -** is not NULL then *pzBuffer is assumed to point to a buffer large enough -** to store the copy of expression p, the copies of p->u.zToken -** (if applicable), and the copies of the p->pLeft and p->pRight expressions, -** if any. Before returning, *pzBuffer is set to the first byte past the -** portion of the buffer copied into by this function. +** An EdupBuf is a memory allocation used to stored multiple Expr objects +** together with their Expr.zToken content. This is used to help implement +** compression while doing sqlite3ExprDup(). The top-level Expr does the +** allocation for itself and many of its decendents, then passes an instance +** of the structure down into exprDup() so that they decendents can have +** access to that memory. */ -static Expr *exprDup(sqlite3 *db, const Expr *p, int dupFlags, u8 **pzBuffer){ +typedef struct EdupBuf EdupBuf; +struct EdupBuf { + u8 *zAlloc; /* Memory space available for storage */ +#ifdef SQLITE_DEBUG + u8 *zEnd; /* First byte past the end of memory */ +#endif +}; + +/* +** This function is similar to sqlite3ExprDup(), except that if pEdupBuf +** is not NULL then it points to memory that can be used to store a copy +** of the input Expr p together with its p->u.zToken (if any). pEdupBuf +** is updated with the new buffer tail prior to returning. +*/ +static Expr *exprDup( + sqlite3 *db, /* Database connection (for memory allocation) */ + const Expr *p, /* Expr tree to be duplicated */ + int dupFlags, /* EXPRDUP_REDUCE for compression. 0 if not */ + EdupBuf *pEdupBuf /* Preallocated storage space, or NULL */ +){ Expr *pNew; /* Value to return */ - u8 *zAlloc; /* Memory space from which to build Expr object */ + EdupBuf sEdupBuf; /* Memory space from which to build Expr object */ u32 staticFlag; /* EP_Static if space not obtained from malloc */ + int nToken = -1; /* Space needed for p->u.zToken. -1 means unknown */ assert( db!=0 ); assert( p ); assert( dupFlags==0 || dupFlags==EXPRDUP_REDUCE ); - assert( pzBuffer==0 || dupFlags==EXPRDUP_REDUCE ); + assert( pEdupBuf==0 || dupFlags==EXPRDUP_REDUCE ); /* Figure out where to write the new Expr structure. */ - if( pzBuffer ){ - zAlloc = *pzBuffer; + if( pEdupBuf ){ + sEdupBuf.zAlloc = pEdupBuf->zAlloc; +#ifdef SQLITE_DEBUG + sEdupBuf.zEnd = pEdupBuf->zEnd; +#endif staticFlag = EP_Static; - assert( zAlloc!=0 ); + assert( sEdupBuf.zAlloc!=0 ); + assert( dupFlags==EXPRDUP_REDUCE ); }else{ - zAlloc = sqlite3DbMallocRawNN(db, dupedExprSize(p, dupFlags)); + int nAlloc; + if( dupFlags ){ + nAlloc = dupedExprSize(p); + }else if( !ExprHasProperty(p, EP_IntValue) && p->u.zToken ){ + nToken = sqlite3Strlen30NN(p->u.zToken)+1; + nAlloc = ROUND8(EXPR_FULLSIZE + nToken); + }else{ + nToken = 0; + nAlloc = ROUND8(EXPR_FULLSIZE); + } + assert( nAlloc==ROUND8(nAlloc) ); + sEdupBuf.zAlloc = sqlite3DbMallocRawNN(db, nAlloc); +#ifdef SQLITE_DEBUG + sEdupBuf.zEnd = sEdupBuf.zAlloc ? sEdupBuf.zAlloc+nAlloc : 0; +#endif + staticFlag = 0; } - pNew = (Expr *)zAlloc; + pNew = (Expr *)sEdupBuf.zAlloc; + assert( EIGHT_BYTE_ALIGNMENT(pNew) ); if( pNew ){ /* Set nNewSize to the size allocated for the structure pointed to @@ -108907,22 +109963,27 @@ static Expr *exprDup(sqlite3 *db, const Expr *p, int dupFlags, u8 **pzBuffer){ ** by the copy of the p->u.zToken string (if any). */ const unsigned nStructSize = dupedExprStructSize(p, dupFlags); - const int nNewSize = nStructSize & 0xfff; - int nToken; - if( !ExprHasProperty(p, EP_IntValue) && p->u.zToken ){ - nToken = sqlite3Strlen30(p->u.zToken) + 1; - }else{ - nToken = 0; + int nNewSize = nStructSize & 0xfff; + if( nToken<0 ){ + if( !ExprHasProperty(p, EP_IntValue) && p->u.zToken ){ + nToken = sqlite3Strlen30(p->u.zToken) + 1; + }else{ + nToken = 0; + } } if( dupFlags ){ + assert( (int)(sEdupBuf.zEnd - sEdupBuf.zAlloc) >= nNewSize+nToken ); assert( ExprHasProperty(p, EP_Reduced)==0 ); - memcpy(zAlloc, p, nNewSize); + memcpy(sEdupBuf.zAlloc, p, nNewSize); }else{ u32 nSize = (u32)exprStructSize(p); - memcpy(zAlloc, p, nSize); + assert( (int)(sEdupBuf.zEnd - sEdupBuf.zAlloc) >= + (int)EXPR_FULLSIZE+nToken ); + memcpy(sEdupBuf.zAlloc, p, nSize); if( nSizeu.zToken string, if any. */ - if( nToken ){ - char *zToken = pNew->u.zToken = (char*)&zAlloc[nNewSize]; + assert( nToken>=0 ); + if( nToken>0 ){ + char *zToken = pNew->u.zToken = (char*)&sEdupBuf.zAlloc[nNewSize]; memcpy(zToken, p->u.zToken, nToken); + nNewSize += nToken; } + sEdupBuf.zAlloc += ROUND8(nNewSize); + + if( ((p->flags|pNew->flags)&(EP_TokenOnly|EP_Leaf))==0 ){ - if( 0==((p->flags|pNew->flags) & (EP_TokenOnly|EP_Leaf)) ){ /* Fill in the pNew->x.pSelect or pNew->x.pList member. */ if( ExprUseXSelect(p) ){ pNew->x.pSelect = sqlite3SelectDup(db, p->x.pSelect, dupFlags); }else{ - pNew->x.pList = sqlite3ExprListDup(db, p->x.pList, dupFlags); + pNew->x.pList = sqlite3ExprListDup(db, p->x.pList, + p->op!=TK_ORDER ? dupFlags : 0); } - } - /* Fill in pNew->pLeft and pNew->pRight. */ - if( ExprHasProperty(pNew, EP_Reduced|EP_TokenOnly|EP_WinFunc) ){ - zAlloc += dupedExprNodeSize(p, dupFlags); - if( !ExprHasProperty(pNew, EP_TokenOnly|EP_Leaf) ){ - pNew->pLeft = p->pLeft ? - exprDup(db, p->pLeft, EXPRDUP_REDUCE, &zAlloc) : 0; - pNew->pRight = p->pRight ? - exprDup(db, p->pRight, EXPRDUP_REDUCE, &zAlloc) : 0; - } #ifndef SQLITE_OMIT_WINDOWFUNC if( ExprHasProperty(p, EP_WinFunc) ){ pNew->y.pWin = sqlite3WindowDup(db, pNew, p->y.pWin); assert( ExprHasProperty(pNew, EP_WinFunc) ); } #endif /* SQLITE_OMIT_WINDOWFUNC */ - if( pzBuffer ){ - *pzBuffer = zAlloc; - } - }else{ - if( !ExprHasProperty(p, EP_TokenOnly|EP_Leaf) ){ - if( pNew->op==TK_SELECT_COLUMN ){ + + /* Fill in pNew->pLeft and pNew->pRight. */ + if( dupFlags ){ + if( p->op==TK_SELECT_COLUMN ){ pNew->pLeft = p->pLeft; - assert( p->pRight==0 || p->pRight==p->pLeft - || ExprHasProperty(p->pLeft, EP_Subquery) ); + assert( p->pRight==0 + || p->pRight==p->pLeft + || ExprHasProperty(p->pLeft, EP_Subquery) ); + }else{ + pNew->pLeft = p->pLeft ? + exprDup(db, p->pLeft, EXPRDUP_REDUCE, &sEdupBuf) : 0; + } + pNew->pRight = p->pRight ? + exprDup(db, p->pRight, EXPRDUP_REDUCE, &sEdupBuf) : 0; + }else{ + if( p->op==TK_SELECT_COLUMN ){ + pNew->pLeft = p->pLeft; + assert( p->pRight==0 + || p->pRight==p->pLeft + || ExprHasProperty(p->pLeft, EP_Subquery) ); }else{ pNew->pLeft = sqlite3ExprDup(db, p->pLeft, 0); } @@ -108980,6 +110047,8 @@ static Expr *exprDup(sqlite3 *db, const Expr *p, int dupFlags, u8 **pzBuffer){ } } } + if( pEdupBuf ) memcpy(pEdupBuf, &sEdupBuf, sizeof(sEdupBuf)); + assert( sEdupBuf.zAlloc <= sEdupBuf.zEnd ); return pNew; } @@ -109244,11 +110313,7 @@ SQLITE_PRIVATE Select *sqlite3SelectDup(sqlite3 *db, const Select *p, int flags) ** initially NULL, then create a new expression list. ** ** The pList argument must be either NULL or a pointer to an ExprList -** obtained from a prior call to sqlite3ExprListAppend(). This routine -** may not be used with an ExprList obtained from sqlite3ExprListDup(). -** Reason: This routine assumes that the number of slots in pList->a[] -** is a power of two. That is true for sqlite3ExprListAppend() returns -** but is not necessarily true from the return value of sqlite3ExprListDup(). +** obtained from a prior call to sqlite3ExprListAppend(). ** ** If a memory allocation error occurs, the entire list is freed and ** NULL is returned. If non-NULL is returned, then it is guaranteed @@ -109513,6 +110578,9 @@ static SQLITE_NOINLINE void exprListDeleteNN(sqlite3 *db, ExprList *pList){ SQLITE_PRIVATE void sqlite3ExprListDelete(sqlite3 *db, ExprList *pList){ if( pList ) exprListDeleteNN(db, pList); } +SQLITE_PRIVATE void sqlite3ExprListDeleteGeneric(sqlite3 *db, void *pList){ + if( ALWAYS(pList) ) exprListDeleteNN(db, (ExprList*)pList); +} /* ** Return the bitwise-OR of all Expr.flags fields in the given @@ -110012,9 +111080,10 @@ SQLITE_PRIVATE int sqlite3ExprCanBeNull(const Expr *p){ case TK_COLUMN: assert( ExprUseYTab(p) ); return ExprHasProperty(p, EP_CanBeNull) || - p->y.pTab==0 || /* Reference to column of index on expression */ + NEVER(p->y.pTab==0) || /* Reference to column of index on expr */ (p->iColumn>=0 && p->y.pTab->aCol!=0 /* Possible due to prior error */ + && ALWAYS(p->iColumny.pTab->nCol) && p->y.pTab->aCol[p->iColumn].notNull==0); default: return 1; @@ -110074,6 +111143,27 @@ SQLITE_PRIVATE int sqlite3IsRowid(const char *z){ return 0; } +/* +** Return a pointer to a buffer containing a usable rowid alias for table +** pTab. An alias is usable if there is not an explicit user-defined column +** of the same name. +*/ +SQLITE_PRIVATE const char *sqlite3RowidAlias(Table *pTab){ + const char *azOpt[] = {"_ROWID_", "ROWID", "OID"}; + int ii; + assert( VisibleRowid(pTab) ); + for(ii=0; iinCol; iCol++){ + if( sqlite3_stricmp(azOpt[ii], pTab->aCol[iCol].zCnName)==0 ) break; + } + if( iCol==pTab->nCol ){ + return azOpt[ii]; + } + } + return 0; +} + /* ** pX is the RHS of an IN operator. If pX is a SELECT statement ** that can be simplified to a direct table access, then return @@ -111611,6 +112701,41 @@ static SQLITE_NOINLINE int sqlite3IndexedExprLookup( } +/* +** Expresion pExpr is guaranteed to be a TK_COLUMN or equivalent. This +** function checks the Parse.pIdxPartExpr list to see if this column +** can be replaced with a constant value. If so, it generates code to +** put the constant value in a register (ideally, but not necessarily, +** register iTarget) and returns the register number. +** +** Or, if the TK_COLUMN cannot be replaced by a constant, zero is +** returned. +*/ +static int exprPartidxExprLookup(Parse *pParse, Expr *pExpr, int iTarget){ + IndexedExpr *p; + for(p=pParse->pIdxPartExpr; p; p=p->pIENext){ + if( pExpr->iColumn==p->iIdxCol && pExpr->iTable==p->iDataCur ){ + Vdbe *v = pParse->pVdbe; + int addr = 0; + int ret; + + if( p->bMaybeNullRow ){ + addr = sqlite3VdbeAddOp1(v, OP_IfNullRow, p->iIdxCur); + } + ret = sqlite3ExprCodeTarget(pParse, p->pExpr, iTarget); + sqlite3VdbeAddOp4(pParse->pVdbe, OP_Affinity, ret, 1, 0, + (const char*)&p->aff, 1); + if( addr ){ + sqlite3VdbeJumpHere(v, addr); + sqlite3VdbeChangeP3(v, addr, ret); + } + return ret; + } + } + return 0; +} + + /* ** Generate code into the current Vdbe to evaluate the given ** expression. Attempt to store the results in register "target". @@ -111647,6 +112772,7 @@ expr_code_doover: assert( !ExprHasVVAProperty(pExpr,EP_Immutable) ); op = pExpr->op; } + assert( op!=TK_ORDER ); switch( op ){ case TK_AGG_COLUMN: { AggInfo *pAggInfo = pExpr->pAggInfo; @@ -111660,7 +112786,7 @@ expr_code_doover: #ifdef SQLITE_VDBE_COVERAGE /* Verify that the OP_Null above is exercised by tests ** tag-20230325-2 */ - sqlite3VdbeAddOp2(v, OP_NotNull, target, 1); + sqlite3VdbeAddOp3(v, OP_NotNull, target, 1, 20230325); VdbeCoverageNeverTaken(v); #endif break; @@ -111768,6 +112894,11 @@ expr_code_doover: iTab = pParse->iSelfTab - 1; } } + else if( pParse->pIdxPartExpr + && 0!=(r1 = exprPartidxExprLookup(pParse, pExpr, target)) + ){ + return r1; + } assert( ExprUseYTab(pExpr) ); assert( pExpr->y.pTab!=0 ); iReg = sqlite3ExprCodeGetColumn(pParse, pExpr->y.pTab, @@ -112428,7 +113559,7 @@ expr_code_doover: ** once. If no functions are involved, then factor the code out and put it at ** the end of the prepared statement in the initialization section. ** -** If regDest>=0 then the result is always stored in that register and the +** If regDest>0 then the result is always stored in that register and the ** result is not reusable. If regDest<0 then this routine is free to ** store the value wherever it wants. The register where the expression ** is stored is returned. When regDest<0, two identical expressions might @@ -112443,6 +113574,7 @@ SQLITE_PRIVATE int sqlite3ExprCodeRunJustOnce( ){ ExprList *p; assert( ConstFactorOk(pParse) ); + assert( regDest!=0 ); p = pParse->pConstExpr; if( regDest<0 && p ){ struct ExprList_item *pItem; @@ -112533,8 +113665,10 @@ SQLITE_PRIVATE void sqlite3ExprCode(Parse *pParse, Expr *pExpr, int target){ inReg = sqlite3ExprCodeTarget(pParse, pExpr, target); if( inReg!=target ){ u8 op; - if( ALWAYS(pExpr) - && (ExprHasProperty(pExpr,EP_Subquery) || pExpr->op==TK_REGISTER) + Expr *pX = sqlite3ExprSkipCollateAndLikely(pExpr); + testcase( pX!=pExpr ); + if( ALWAYS(pX) + && (ExprHasProperty(pX,EP_Subquery) || pX->op==TK_REGISTER) ){ op = OP_Copy; }else{ @@ -113254,8 +114388,8 @@ SQLITE_PRIVATE int sqlite3ExprListCompare(const ExprList *pA, const ExprList *pB */ SQLITE_PRIVATE int sqlite3ExprCompareSkip(Expr *pA,Expr *pB, int iTab){ return sqlite3ExprCompare(0, - sqlite3ExprSkipCollateAndLikely(pA), - sqlite3ExprSkipCollateAndLikely(pB), + sqlite3ExprSkipCollate(pA), + sqlite3ExprSkipCollate(pB), iTab); } @@ -113727,6 +114861,12 @@ SQLITE_PRIVATE int sqlite3ReferencesSrcList(Parse *pParse, Expr *pExpr, SrcList assert( pExpr->op==TK_AGG_FUNCTION ); assert( ExprUseXList(pExpr) ); sqlite3WalkExprList(&w, pExpr->x.pList); + if( pExpr->pLeft ){ + assert( pExpr->pLeft->op==TK_ORDER ); + assert( ExprUseXList(pExpr->pLeft) ); + assert( pExpr->pLeft->x.pList!=0 ); + sqlite3WalkExprList(&w, pExpr->pLeft->x.pList); + } #ifndef SQLITE_OMIT_WINDOWFUNC if( ExprHasProperty(pExpr, EP_WinFunc) ){ sqlite3WalkExpr(&w, pExpr->y.pWin->pFilter); @@ -113974,13 +115114,14 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){ case TK_AGG_FUNCTION: { if( (pNC->ncFlags & NC_InAggFunc)==0 && pWalker->walkerDepth==pExpr->op2 + && pExpr->pAggInfo==0 ){ /* Check to see if pExpr is a duplicate of another aggregate ** function that is already in the pAggInfo structure */ struct AggInfo_func *pItem = pAggInfo->aFunc; for(i=0; inFunc; i++, pItem++){ - if( pItem->pFExpr==pExpr ) break; + if( NEVER(pItem->pFExpr==pExpr) ) break; if( sqlite3ExprCompare(0, pItem->pFExpr, pExpr, -1)==0 ){ break; } @@ -113991,14 +115132,44 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){ u8 enc = ENC(pParse->db); i = addAggInfoFunc(pParse->db, pAggInfo); if( i>=0 ){ + int nArg; assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); pItem = &pAggInfo->aFunc[i]; pItem->pFExpr = pExpr; assert( ExprUseUToken(pExpr) ); + nArg = pExpr->x.pList ? pExpr->x.pList->nExpr : 0; pItem->pFunc = sqlite3FindFunction(pParse->db, - pExpr->u.zToken, - pExpr->x.pList ? pExpr->x.pList->nExpr : 0, enc, 0); - if( pExpr->flags & EP_Distinct ){ + pExpr->u.zToken, nArg, enc, 0); + assert( pItem->bOBUnique==0 ); + if( pExpr->pLeft + && (pItem->pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL)==0 + ){ + /* The NEEDCOLL test above causes any ORDER BY clause on + ** aggregate min() or max() to be ignored. */ + ExprList *pOBList; + assert( nArg>0 ); + assert( pExpr->pLeft->op==TK_ORDER ); + assert( ExprUseXList(pExpr->pLeft) ); + pItem->iOBTab = pParse->nTab++; + pOBList = pExpr->pLeft->x.pList; + assert( pOBList->nExpr>0 ); + assert( pItem->bOBUnique==0 ); + if( pOBList->nExpr==1 + && nArg==1 + && sqlite3ExprCompare(0,pOBList->a[0].pExpr, + pExpr->x.pList->a[0].pExpr,0)==0 + ){ + pItem->bOBPayload = 0; + pItem->bOBUnique = ExprHasProperty(pExpr, EP_Distinct); + }else{ + pItem->bOBPayload = 1; + } + pItem->bUseSubtype = + (pItem->pFunc->funcFlags & SQLITE_SUBTYPE)!=0; + }else{ + pItem->iOBTab = -1; + } + if( ExprHasProperty(pExpr, EP_Distinct) && !pItem->bOBUnique ){ pItem->iDistinct = pParse->nTab++; }else{ pItem->iDistinct = -1; @@ -114634,14 +115805,19 @@ SQLITE_PRIVATE void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){ /* Verify that constraints are still satisfied */ if( pNew->pCheck!=0 || (pCol->notNull && (pCol->colFlags & COLFLAG_GENERATED)!=0) + || (pTab->tabFlags & TF_Strict)!=0 ){ sqlite3NestedParse(pParse, "SELECT CASE WHEN quick_check GLOB 'CHECK*'" " THEN raise(ABORT,'CHECK constraint failed')" + " WHEN quick_check GLOB 'non-* value in*'" + " THEN raise(ABORT,'type mismatch on DEFAULT')" " ELSE raise(ABORT,'NOT NULL constraint failed')" " END" " FROM pragma_quick_check(%Q,%Q)" - " WHERE quick_check GLOB 'CHECK*' OR quick_check GLOB 'NULL*'", + " WHERE quick_check GLOB 'CHECK*'" + " OR quick_check GLOB 'NULL*'" + " OR quick_check GLOB 'non-* value in*'", zTab, zDb ); } @@ -116756,9 +117932,9 @@ static void openStatTable( typedef struct StatAccum StatAccum; typedef struct StatSample StatSample; struct StatSample { - tRowcnt *anEq; /* sqlite_stat4.nEq */ tRowcnt *anDLt; /* sqlite_stat4.nDLt */ #ifdef SQLITE_ENABLE_STAT4 + tRowcnt *anEq; /* sqlite_stat4.nEq */ tRowcnt *anLt; /* sqlite_stat4.nLt */ union { i64 iRowid; /* Rowid in main table of the key */ @@ -116916,9 +118092,9 @@ static void statInit( /* Allocate the space required for the StatAccum object */ n = sizeof(*p) - + sizeof(tRowcnt)*nColUp /* StatAccum.anEq */ - + sizeof(tRowcnt)*nColUp; /* StatAccum.anDLt */ + + sizeof(tRowcnt)*nColUp; /* StatAccum.anDLt */ #ifdef SQLITE_ENABLE_STAT4 + n += sizeof(tRowcnt)*nColUp; /* StatAccum.anEq */ if( mxSample ){ n += sizeof(tRowcnt)*nColUp /* StatAccum.anLt */ + sizeof(StatSample)*(nCol+mxSample) /* StatAccum.aBest[], a[] */ @@ -116939,9 +118115,9 @@ static void statInit( p->nKeyCol = nKeyCol; p->nSkipAhead = 0; p->current.anDLt = (tRowcnt*)&p[1]; - p->current.anEq = &p->current.anDLt[nColUp]; #ifdef SQLITE_ENABLE_STAT4 + p->current.anEq = &p->current.anDLt[nColUp]; p->mxSample = p->nLimit==0 ? mxSample : 0; if( mxSample ){ u8 *pSpace; /* Allocated space not yet assigned */ @@ -117208,7 +118384,9 @@ static void statPush( if( p->nRow==0 ){ /* This is the first call to this function. Do initialization. */ +#ifdef SQLITE_ENABLE_STAT4 for(i=0; inCol; i++) p->current.anEq[i] = 1; +#endif }else{ /* Second and subsequent calls get processed here */ #ifdef SQLITE_ENABLE_STAT4 @@ -117217,15 +118395,17 @@ static void statPush( /* Update anDLt[], anLt[] and anEq[] to reflect the values that apply ** to the current row of the index. */ +#ifdef SQLITE_ENABLE_STAT4 for(i=0; icurrent.anEq[i]++; } +#endif for(i=iChng; inCol; i++){ p->current.anDLt[i]++; #ifdef SQLITE_ENABLE_STAT4 if( p->mxSample ) p->current.anLt[i] += p->current.anEq[i]; -#endif p->current.anEq[i] = 1; +#endif } } @@ -117359,7 +118539,9 @@ static void statGet( u64 iVal = (p->nRow + nDistinct - 1) / nDistinct; if( iVal==2 && p->nRow*10 <= nDistinct*11 ) iVal = 1; sqlite3_str_appendf(&sStat, " %llu", iVal); +#ifdef SQLITE_ENABLE_STAT4 assert( p->current.anEq[i] ); +#endif } sqlite3ResultStrAccum(context, &sStat); } @@ -118048,6 +119230,16 @@ static void decodeIntArray( while( z[0]!=0 && z[0]!=' ' ) z++; while( z[0]==' ' ) z++; } + + /* Set the bLowQual flag if the peak number of rows obtained + ** from a full equality match is so large that a full table scan + ** seems likely to be faster than using the index. + */ + if( aLog[0] > 66 /* Index has more than 100 rows */ + && aLog[0] <= aLog[nOut-1] /* And only a single value seen */ + ){ + pIndex->bLowQual = 1; + } } } @@ -119620,19 +120812,14 @@ SQLITE_PRIVATE void sqlite3FinishCoding(Parse *pParse){ */ if( pParse->pAinc ) sqlite3AutoincrementBegin(pParse); - /* Code constant expressions that where factored out of inner loops. - ** - ** The pConstExpr list might also contain expressions that we simply - ** want to keep around until the Parse object is deleted. Such - ** expressions have iConstExprReg==0. Do not generate code for - ** those expressions, of course. + /* Code constant expressions that were factored out of inner loops. */ if( pParse->pConstExpr ){ ExprList *pEL = pParse->pConstExpr; pParse->okConstFactor = 0; for(i=0; inExpr; i++){ - int iReg = pEL->a[i].u.iConstExprReg; - sqlite3ExprCode(pParse, pEL->a[i].pExpr, iReg); + assert( pEL->a[i].u.iConstExprReg>0 ); + sqlite3ExprCode(pParse, pEL->a[i].pExpr, pEL->a[i].u.iConstExprReg); } } @@ -120099,7 +121286,7 @@ SQLITE_PRIVATE void sqlite3ColumnSetExpr( */ SQLITE_PRIVATE Expr *sqlite3ColumnExpr(Table *pTab, Column *pCol){ if( pCol->iDflt==0 ) return 0; - if( NEVER(!IsOrdinaryTable(pTab)) ) return 0; + if( !IsOrdinaryTable(pTab) ) return 0; if( NEVER(pTab->u.tab.pDfltList==0) ) return 0; if( NEVER(pTab->u.tab.pDfltList->nExpriDflt) ) return 0; return pTab->u.tab.pDfltList->a[pCol->iDflt-1].pExpr; @@ -120252,6 +121439,9 @@ SQLITE_PRIVATE void sqlite3DeleteTable(sqlite3 *db, Table *pTable){ if( db->pnBytesFreed==0 && (--pTable->nTabRef)>0 ) return; deleteTable(db, pTable); } +SQLITE_PRIVATE void sqlite3DeleteTableGeneric(sqlite3 *db, void *pTable){ + sqlite3DeleteTable(db, (Table*)pTable); +} /* @@ -120786,20 +121976,14 @@ SQLITE_PRIVATE void sqlite3ColumnPropertiesFromName(Table *pTab, Column *pCol){ } #endif -/* -** Name of the special TEMP trigger used to implement RETURNING. The -** name begins with "sqlite_" so that it is guaranteed not to collide -** with any application-generated triggers. -*/ -#define RETURNING_TRIGGER_NAME "sqlite_returning" - /* ** Clean up the data structures associated with the RETURNING clause. */ -static void sqlite3DeleteReturning(sqlite3 *db, Returning *pRet){ +static void sqlite3DeleteReturning(sqlite3 *db, void *pArg){ + Returning *pRet = (Returning*)pArg; Hash *pHash; pHash = &(db->aDb[1].pSchema->trigHash); - sqlite3HashInsert(pHash, RETURNING_TRIGGER_NAME, 0); + sqlite3HashInsert(pHash, pRet->zName, 0); sqlite3ExprListDelete(db, pRet->pReturnEL); sqlite3DbFree(db, pRet); } @@ -120838,11 +122022,12 @@ SQLITE_PRIVATE void sqlite3AddReturning(Parse *pParse, ExprList *pList){ pParse->u1.pReturning = pRet; pRet->pParse = pParse; pRet->pReturnEL = pList; - sqlite3ParserAddCleanup(pParse, - (void(*)(sqlite3*,void*))sqlite3DeleteReturning, pRet); + sqlite3ParserAddCleanup(pParse, sqlite3DeleteReturning, pRet); testcase( pParse->earlyCleanup ); if( db->mallocFailed ) return; - pRet->retTrig.zName = RETURNING_TRIGGER_NAME; + sqlite3_snprintf(sizeof(pRet->zName), pRet->zName, + "sqlite_returning_%p", pParse); + pRet->retTrig.zName = pRet->zName; pRet->retTrig.op = TK_RETURNING; pRet->retTrig.tr_tm = TRIGGER_AFTER; pRet->retTrig.bReturning = 1; @@ -120853,9 +122038,9 @@ SQLITE_PRIVATE void sqlite3AddReturning(Parse *pParse, ExprList *pList){ pRet->retTStep.pTrig = &pRet->retTrig; pRet->retTStep.pExprList = pList; pHash = &(db->aDb[1].pSchema->trigHash); - assert( sqlite3HashFind(pHash, RETURNING_TRIGGER_NAME)==0 + assert( sqlite3HashFind(pHash, pRet->zName)==0 || pParse->nErr || pParse->ifNotExists ); - if( sqlite3HashInsert(pHash, RETURNING_TRIGGER_NAME, &pRet->retTrig) + if( sqlite3HashInsert(pHash, pRet->zName, &pRet->retTrig) ==&pRet->retTrig ){ sqlite3OomFault(db); } @@ -121036,7 +122221,8 @@ SQLITE_PRIVATE char sqlite3AffinityType(const char *zIn, Column *pCol){ assert( zIn!=0 ); while( zIn[0] ){ - h = (h<<8) + sqlite3UpperToLower[(*zIn)&0xff]; + u8 x = *(u8*)zIn; + h = (h<<8) + sqlite3UpperToLower[x]; zIn++; if( h==(('c'<<24)+('h'<<16)+('a'<<8)+'r') ){ /* CHAR */ aff = SQLITE_AFF_TEXT; @@ -122299,6 +123485,17 @@ SQLITE_PRIVATE void sqlite3EndTable( /* Reparse everything to update our internal data structures */ sqlite3VdbeAddParseSchemaOp(v, iDb, sqlite3MPrintf(db, "tbl_name='%q' AND type!='trigger'", p->zName),0); + + /* Test for cycles in generated columns and illegal expressions + ** in CHECK constraints and in DEFAULT clauses. */ + if( p->tabFlags & TF_HasGenerated ){ + sqlite3VdbeAddOp4(v, OP_SqlExec, 1, 0, 0, + sqlite3MPrintf(db, "SELECT*FROM\"%w\".\"%w\"", + db->aDb[iDb].zDbSName, p->zName), P4_DYNAMIC); + } + sqlite3VdbeAddOp4(v, OP_SqlExec, 1, 0, 0, + sqlite3MPrintf(db, "PRAGMA \"%w\".integrity_check(%Q)", + db->aDb[iDb].zDbSName, p->zName), P4_DYNAMIC); } /* Add the table to the in-memory representation of the database. @@ -124890,7 +126087,7 @@ SQLITE_PRIVATE void sqlite3Reindex(Parse *pParse, Token *pName1, Token *pName2){ if( iDb<0 ) return; z = sqlite3NameFromToken(db, pObjName); if( z==0 ) return; - zDb = db->aDb[iDb].zDbSName; + zDb = pName2->n ? db->aDb[iDb].zDbSName : 0; pTab = sqlite3FindTable(db, z, zDb); if( pTab ){ reindexTable(pParse, pTab, 0); @@ -124900,6 +126097,7 @@ SQLITE_PRIVATE void sqlite3Reindex(Parse *pParse, Token *pName1, Token *pName2){ pIndex = sqlite3FindIndex(db, z, zDb); sqlite3DbFree(db, z); if( pIndex ){ + iDb = sqlite3SchemaToIndex(db, pIndex->pTable->pSchema); sqlite3BeginWriteOperation(pParse, 0, iDb); sqlite3RefillIndex(pParse, pIndex, -1); return; @@ -125065,6 +126263,9 @@ SQLITE_PRIVATE void sqlite3WithDelete(sqlite3 *db, With *pWith){ sqlite3DbFree(db, pWith); } } +SQLITE_PRIVATE void sqlite3WithDeleteGeneric(sqlite3 *db, void *pWith){ + sqlite3WithDelete(db, (With*)pWith); +} #endif /* !defined(SQLITE_OMIT_CTE) */ /************** End of build.c ***********************************************/ @@ -127901,7 +129102,8 @@ static void hexFunc( *(z++) = hexdigits[c&0xf]; } *z = 0; - sqlite3_result_text(context, zHex, n*2, sqlite3_free); + sqlite3_result_text64(context, zHex, (u64)(z-zHex), + sqlite3_free, SQLITE_UTF8); } } @@ -128195,6 +129397,81 @@ static void trimFunc( sqlite3_result_text(context, (char*)zIn, nIn, SQLITE_TRANSIENT); } +/* The core implementation of the CONCAT(...) and CONCAT_WS(SEP,...) +** functions. +** +** Return a string value that is the concatenation of all non-null +** entries in argv[]. Use zSep as the separator. +*/ +static void concatFuncCore( + sqlite3_context *context, + int argc, + sqlite3_value **argv, + int nSep, + const char *zSep +){ + i64 j, k, n = 0; + int i; + char *z; + for(i=0; i0 ){ + const char *v = (const char*)sqlite3_value_text(argv[i]); + if( v!=0 ){ + if( j>0 && nSep>0 ){ + memcpy(&z[j], zSep, nSep); + j += nSep; + } + memcpy(&z[j], v, k); + j += k; + } + } + } + z[j] = 0; + assert( j<=n ); + sqlite3_result_text64(context, z, j, sqlite3_free, SQLITE_UTF8); +} + +/* +** The CONCAT(...) function. Generate a string result that is the +** concatentation of all non-null arguments. +*/ +static void concatFunc( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + concatFuncCore(context, argc, argv, 0, ""); +} + +/* +** The CONCAT_WS(separator, ...) function. +** +** Generate a string that is the concatenation of 2nd through the Nth +** argument. Use the first argument (which must be non-NULL) as the +** separator. +*/ +static void concatwsFunc( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + int nSep = sqlite3_value_bytes(argv[0]); + const char *zSep = (const char*)sqlite3_value_text(argv[0]); + if( zSep==0 ) return; + concatFuncCore(context, argc-1, argv+1, nSep, zSep); +} + #ifdef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION /* @@ -128616,6 +129893,7 @@ static void minMaxFinalize(sqlite3_context *context){ /* ** group_concat(EXPR, ?SEPARATOR?) +** string_agg(EXPR, SEPARATOR) ** ** The SEPARATOR goes before the EXPR string. This is tragic. The ** groupConcatInverse() implementation would have been easier if the @@ -129206,6 +130484,11 @@ SQLITE_PRIVATE void sqlite3RegisterBuiltinFunctions(void){ FUNCTION(hex, 1, 0, 0, hexFunc ), FUNCTION(unhex, 1, 0, 0, unhexFunc ), FUNCTION(unhex, 2, 0, 0, unhexFunc ), + FUNCTION(concat, -1, 0, 0, concatFunc ), + FUNCTION(concat, 0, 0, 0, 0 ), + FUNCTION(concat_ws, -1, 0, 0, concatwsFunc ), + FUNCTION(concat_ws, 0, 0, 0, 0 ), + FUNCTION(concat_ws, 1, 0, 0, 0 ), INLINE_FUNC(ifnull, 2, INLINEFUNC_coalesce, 0 ), VFUNCTION(random, 0, 0, 0, randomFunc ), VFUNCTION(randomblob, 1, 0, 0, randomBlob ), @@ -129235,6 +130518,8 @@ SQLITE_PRIVATE void sqlite3RegisterBuiltinFunctions(void){ groupConcatFinalize, groupConcatValue, groupConcatInverse, 0), WAGGREGATE(group_concat, 2, 0, 0, groupConcatStep, groupConcatFinalize, groupConcatValue, groupConcatInverse, 0), + WAGGREGATE(string_agg, 2, 0, 0, groupConcatStep, + groupConcatFinalize, groupConcatValue, groupConcatInverse, 0), LIKEFUNC(glob, 2, &globInfo, SQLITE_FUNC_LIKE|SQLITE_FUNC_CASE), #ifdef SQLITE_CASE_SENSITIVE_LIKE @@ -130177,6 +131462,7 @@ static int isSetNullAction(Parse *pParse, FKey *pFKey){ if( (p==pFKey->apTrigger[0] && pFKey->aAction[0]==OE_SetNull) || (p==pFKey->apTrigger[1] && pFKey->aAction[1]==OE_SetNull) ){ + assert( (pTop->db->flags & SQLITE_FkNoAction)==0 ); return 1; } } @@ -130371,6 +131657,8 @@ SQLITE_PRIVATE void sqlite3FkCheck( } if( regOld!=0 ){ int eAction = pFKey->aAction[aChange!=0]; + if( (db->flags & SQLITE_FkNoAction) ) eAction = OE_None; + fkScanChildren(pParse, pSrc, pTab, pIdx, pFKey, aiCol, regOld, 1); /* If this is a deferred FK constraint, or a CASCADE or SET NULL ** action applies, then any foreign key violations caused by @@ -130486,7 +131774,11 @@ SQLITE_PRIVATE int sqlite3FkRequired( /* Check if any parent key columns are being modified. */ for(p=sqlite3FkReferences(pTab); p; p=p->pNextTo){ if( fkParentIsModified(pTab, p, aChange, chngRowid) ){ - if( p->aAction[1]!=OE_None ) return 2; + if( (pParse->db->flags & SQLITE_FkNoAction)==0 + && p->aAction[1]!=OE_None + ){ + return 2; + } bHaveFK = 1; } } @@ -130536,6 +131828,7 @@ static Trigger *fkActionTrigger( int iAction = (pChanges!=0); /* 1 for UPDATE, 0 for DELETE */ action = pFKey->aAction[iAction]; + if( (db->flags & SQLITE_FkNoAction) ) action = OE_None; if( action==OE_Restrict && (db->flags & SQLITE_DeferFKs) ){ return 0; } @@ -134491,6 +135784,9 @@ struct sqlite3_api_routines { int (*is_interrupted)(sqlite3*); /* Version 3.43.0 and later */ int (*stmt_explain)(sqlite3_stmt*,int); + /* Version 3.44.0 and later */ + void *(*get_clientdata)(sqlite3*,const char*); + int (*set_clientdata)(sqlite3*, const char*, void*, void(*)(void*)); }; /* @@ -134821,6 +136117,9 @@ typedef int (*sqlite3_loadext_entry)( #define sqlite3_is_interrupted sqlite3_api->is_interrupted /* Version 3.43.0 and later */ #define sqlite3_stmt_explain sqlite3_api->stmt_explain +/* Version 3.44.0 and later */ +#define sqlite3_get_clientdata sqlite3_api->get_clientdata +#define sqlite3_set_clientdata sqlite3_api->set_clientdata #endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */ #if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) @@ -135339,7 +136638,10 @@ static const sqlite3_api_routines sqlite3Apis = { /* Version 3.41.0 and later */ sqlite3_is_interrupted, /* Version 3.43.0 and later */ - sqlite3_stmt_explain + sqlite3_stmt_explain, + /* Version 3.44.0 and later */ + sqlite3_get_clientdata, + sqlite3_set_clientdata }; /* True if x is the directory separator character @@ -135555,6 +136857,9 @@ SQLITE_PRIVATE void sqlite3CloseExtensions(sqlite3 *db){ ** default so as not to open security holes in older applications. */ SQLITE_API int sqlite3_enable_load_extension(sqlite3 *db, int onoff){ +#ifdef SQLITE_ENABLE_API_ARMOR + if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT; +#endif sqlite3_mutex_enter(db->mutex); if( onoff ){ db->flags |= SQLITE_LoadExtension|SQLITE_LoadExtFunc; @@ -135604,6 +136909,9 @@ SQLITE_API int sqlite3_auto_extension( void (*xInit)(void) ){ int rc = SQLITE_OK; +#ifdef SQLITE_ENABLE_API_ARMOR + if( xInit==0 ) return SQLITE_MISUSE_BKPT; +#endif #ifndef SQLITE_OMIT_AUTOINIT rc = sqlite3_initialize(); if( rc ){ @@ -135656,6 +136964,9 @@ SQLITE_API int sqlite3_cancel_auto_extension( int i; int n = 0; wsdAutoextInit; +#ifdef SQLITE_ENABLE_API_ARMOR + if( xInit==0 ) return 0; +#endif sqlite3_mutex_enter(mutex); for(i=(int)wsdAutoext.nExt-1; i>=0; i--){ if( wsdAutoext.aExt[i]==xInit ){ @@ -137525,7 +138836,11 @@ SQLITE_PRIVATE void sqlite3Pragma( #endif if( sqlite3GetBoolean(zRight, 0) ){ - db->flags |= mask; + if( (mask & SQLITE_WriteSchema)==0 + || (db->flags & SQLITE_Defensive)==0 + ){ + db->flags |= mask; + } }else{ db->flags &= ~mask; if( mask==SQLITE_DeferFKs ) db->nDeferredImmCons = 0; @@ -138158,8 +139473,32 @@ SQLITE_PRIVATE void sqlite3Pragma( int r2; /* Previous key for WITHOUT ROWID tables */ int mxCol; /* Maximum non-virtual column number */ - if( !IsOrdinaryTable(pTab) ) continue; 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( isQuick || HasRowid(pTab) ){ pPk = 0; r2 = 0; @@ -139285,7 +140624,8 @@ static const sqlite3_module pragmaVtabModule = { 0, /* xSavepoint */ 0, /* xRelease */ 0, /* xRollbackTo */ - 0 /* xShadowName */ + 0, /* xShadowName */ + 0 /* xIntegrity */ }; /* @@ -139909,8 +141249,6 @@ SQLITE_PRIVATE void sqlite3ParseObjectReset(Parse *pParse){ db->lookaside.sz = db->lookaside.bDisable ? 0 : db->lookaside.szTrue; assert( pParse->db->pParse==pParse ); db->pParse = pParse->pOuterParse; - pParse->db = 0; - pParse->disableLookaside = 0; } /* @@ -140181,6 +141519,7 @@ static int sqlite3LockAndPrepare( assert( (rc&db->errMask)==rc ); db->busyHandler.nBusy = 0; sqlite3_mutex_leave(db->mutex); + assert( rc==SQLITE_OK || (*ppStmt)==0 ); return rc; } @@ -140578,6 +141917,9 @@ SQLITE_PRIVATE Select *sqlite3SelectNew( SQLITE_PRIVATE void sqlite3SelectDelete(sqlite3 *db, Select *p){ if( OK_IF_ALWAYS_TRUE(p) ) clearSelect(db, p, 1); } +SQLITE_PRIVATE void sqlite3SelectDeleteGeneric(sqlite3 *db, void *p){ + if( ALWAYS(p) ) clearSelect(db, (Select*)p, 1); +} /* ** Return a pointer to the right-most SELECT statement in a compound. @@ -140848,6 +142190,7 @@ static void unsetJoinExpr(Expr *p, int iTable, int nullable){ } if( p->op==TK_FUNCTION ){ assert( ExprUseXList(p) ); + assert( p->pLeft==0 ); if( p->x.pList ){ int i; for(i=0; ix.pList->nExpr; i++){ @@ -142712,7 +144055,8 @@ SQLITE_PRIVATE void sqlite3SubqueryColumnTypes( NameContext sNC; assert( pSelect!=0 ); - assert( (pSelect->selFlags & SF_Resolved)!=0 ); + testcase( (pSelect->selFlags & SF_Resolved)==0 ); + assert( (pSelect->selFlags & SF_Resolved)!=0 || IN_RENAME_OBJECT ); assert( pTab->nCol==pSelect->pEList->nExpr || pParse->nErr>0 ); assert( aff==SQLITE_AFF_NONE || aff==SQLITE_AFF_BLOB ); if( db->mallocFailed || IN_RENAME_OBJECT ) return; @@ -143596,9 +144940,7 @@ multi_select_end: pDest->iSdst = dest.iSdst; pDest->nSdst = dest.nSdst; if( pDelete ){ - sqlite3ParserAddCleanup(pParse, - (void(*)(sqlite3*,void*))sqlite3SelectDelete, - pDelete); + sqlite3ParserAddCleanup(pParse, sqlite3SelectDeleteGeneric, pDelete); } return rc; } @@ -144149,8 +145491,7 @@ static int multiSelectOrderBy( /* Make arrangements to free the 2nd and subsequent arms of the compound ** after the parse has finished */ if( pSplit->pPrior ){ - sqlite3ParserAddCleanup(pParse, - (void(*)(sqlite3*,void*))sqlite3SelectDelete, pSplit->pPrior); + sqlite3ParserAddCleanup(pParse, sqlite3SelectDeleteGeneric, pSplit->pPrior); } pSplit->pPrior = pPrior; pPrior->pNext = pSplit; @@ -144971,9 +146312,7 @@ static int flattenSubquery( Table *pTabToDel = pSubitem->pTab; if( pTabToDel->nTabRef==1 ){ Parse *pToplevel = sqlite3ParseToplevel(pParse); - sqlite3ParserAddCleanup(pToplevel, - (void(*)(sqlite3*,void*))sqlite3DeleteTable, - pTabToDel); + sqlite3ParserAddCleanup(pToplevel, sqlite3DeleteTableGeneric, pTabToDel); testcase( pToplevel->earlyCleanup ); }else{ pTabToDel->nTabRef--; @@ -146020,8 +147359,7 @@ static struct Cte *searchWith( SQLITE_PRIVATE With *sqlite3WithPush(Parse *pParse, With *pWith, u8 bFree){ if( pWith ){ if( bFree ){ - pWith = (With*)sqlite3ParserAddCleanup(pParse, - (void(*)(sqlite3*,void*))sqlite3WithDelete, + pWith = (With*)sqlite3ParserAddCleanup(pParse, sqlite3WithDeleteGeneric, pWith); if( pWith==0 ) return 0; } @@ -146508,6 +147846,7 @@ static int selectExpander(Walker *pWalker, Select *p){ char *zTName = 0; /* text of name of TABLE */ int iErrOfst; if( pE->op==TK_DOT ){ + assert( (selFlags & SF_NestedFrom)==0 ); assert( pE->pLeft!=0 ); assert( !ExprHasProperty(pE->pLeft, EP_IntValue) ); zTName = pE->pLeft->u.zToken; @@ -146518,6 +147857,7 @@ static int selectExpander(Walker *pWalker, Select *p){ iErrOfst = pE->w.iOfst; } for(i=0, pFrom=pTabList->a; inSrc; i++, pFrom++){ + int nAdd; /* Number of cols including rowid */ Table *pTab = pFrom->pTab; /* Table for this data source */ ExprList *pNestedFrom; /* Result-set of a nested FROM clause */ char *zTabName; /* AS name for this data source */ @@ -146535,6 +147875,7 @@ static int selectExpander(Walker *pWalker, Select *p){ pNestedFrom = pFrom->pSelect->pEList; assert( pNestedFrom!=0 ); assert( pNestedFrom->nExpr==pTab->nCol ); + assert( VisibleRowid(pTab)==0 ); }else{ if( zTName && sqlite3StrICmp(zTName, zTabName)!=0 ){ continue; @@ -146565,33 +147906,48 @@ static int selectExpander(Walker *pWalker, Select *p){ }else{ pUsing = 0; } - for(j=0; jnCol; j++){ - char *zName = pTab->aCol[j].zCnName; + + nAdd = pTab->nCol + (VisibleRowid(pTab) && (selFlags&SF_NestedFrom)); + for(j=0; ja[j], 0, zTName, 0)==0 - ){ - continue; - } + if( j==pTab->nCol ){ + zName = sqlite3RowidAlias(pTab); + if( zName==0 ) continue; + }else{ + zName = pTab->aCol[j].zCnName; - /* If a column is marked as 'hidden', omit it from the expanded - ** result-set list unless the SELECT has the SF_IncludeHidden - ** bit set. - */ - if( (p->selFlags & SF_IncludeHidden)==0 - && IsHiddenColumn(&pTab->aCol[j]) - ){ - continue; - } - if( (pTab->aCol[j].colFlags & COLFLAG_NOEXPAND)!=0 - && zTName==0 - && (selFlags & (SF_NestedFrom))==0 - ){ - continue; + /* If pTab is actually an SF_NestedFrom sub-select, do not + ** expand any ENAME_ROWID columns. */ + if( pNestedFrom && pNestedFrom->a[j].fg.eEName==ENAME_ROWID ){ + continue; + } + + if( zTName + && pNestedFrom + && sqlite3MatchEName(&pNestedFrom->a[j], 0, zTName, 0, 0)==0 + ){ + continue; + } + + /* If a column is marked as 'hidden', omit it from the expanded + ** result-set list unless the SELECT has the SF_IncludeHidden + ** bit set. + */ + if( (p->selFlags & SF_IncludeHidden)==0 + && IsHiddenColumn(&pTab->aCol[j]) + ){ + continue; + } + if( (pTab->aCol[j].colFlags & COLFLAG_NOEXPAND)!=0 + && zTName==0 + && (selFlags & (SF_NestedFrom))==0 + ){ + continue; + } } + assert( zName ); tableSeen = 1; if( i>0 && zTName==0 && (selFlags & SF_NestedFrom)==0 ){ @@ -146641,11 +147997,11 @@ static int selectExpander(Walker *pWalker, Select *p){ zSchemaName, zTabName, zName); testcase( pX->zEName==0 ); } - pX->fg.eEName = ENAME_TAB; + pX->fg.eEName = (j==pTab->nCol ? ENAME_ROWID : ENAME_TAB); if( (pFrom->fg.isUsing && sqlite3IdListIndex(pFrom->u3.pUsing, zName)>=0) || (pUsing && sqlite3IdListIndex(pUsing, zName)>=0) - || (pTab->aCol[j].colFlags & COLFLAG_NOEXPAND)!=0 + || (jnCol && (pTab->aCol[j].colFlags & COLFLAG_NOEXPAND)) ){ pX->fg.bNoExpand = 1; } @@ -146747,10 +148103,11 @@ static void selectAddSubqueryTypeInfo(Walker *pWalker, Select *p){ SrcList *pTabList; SrcItem *pFrom; - assert( p->selFlags & SF_Resolved ); if( p->selFlags & SF_HasTypeInfo ) return; p->selFlags |= SF_HasTypeInfo; pParse = pWalker->pParse; + testcase( (p->selFlags & SF_Resolved)==0 ); + assert( (p->selFlags & SF_Resolved) || IN_RENAME_OBJECT ); pTabList = p->pSrc; for(i=0, pFrom=pTabList->a; inSrc; i++, pFrom++){ Table *pTab = pFrom->pTab; @@ -146866,8 +148223,14 @@ static void analyzeAggFuncArgs( pNC->ncFlags |= NC_InAggFunc; for(i=0; inFunc; i++){ Expr *pExpr = pAggInfo->aFunc[i].pFExpr; + assert( pExpr->op==TK_FUNCTION || pExpr->op==TK_AGG_FUNCTION ); assert( ExprUseXList(pExpr) ); sqlite3ExprAnalyzeAggList(pNC, pExpr->x.pList); + if( pExpr->pLeft ){ + assert( pExpr->pLeft->op==TK_ORDER ); + assert( ExprUseXList(pExpr->pLeft) ); + sqlite3ExprAnalyzeAggList(pNC, pExpr->pLeft->x.pList); + } #ifndef SQLITE_OMIT_WINDOWFUNC assert( !IsWindowFunc(pExpr) ); if( ExprHasProperty(pExpr, EP_WinFunc) ){ @@ -147022,6 +148385,36 @@ static void resetAccumulator(Parse *pParse, AggInfo *pAggInfo){ pFunc->pFunc->zName)); } } + if( pFunc->iOBTab>=0 ){ + ExprList *pOBList; + KeyInfo *pKeyInfo; + int nExtra = 0; + assert( pFunc->pFExpr->pLeft!=0 ); + assert( pFunc->pFExpr->pLeft->op==TK_ORDER ); + assert( ExprUseXList(pFunc->pFExpr->pLeft) ); + assert( pFunc->pFunc!=0 ); + pOBList = pFunc->pFExpr->pLeft->x.pList; + if( !pFunc->bOBUnique ){ + nExtra++; /* One extra column for the OP_Sequence */ + } + if( pFunc->bOBPayload ){ + /* extra columns for the function arguments */ + assert( ExprUseXList(pFunc->pFExpr) ); + nExtra += pFunc->pFExpr->x.pList->nExpr; + } + if( pFunc->bUseSubtype ){ + nExtra += pFunc->pFExpr->x.pList->nExpr; + } + pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pOBList, 0, nExtra); + if( !pFunc->bOBUnique && pParse->nErr==0 ){ + pKeyInfo->nKeyField++; + } + sqlite3VdbeAddOp4(v, OP_OpenEphemeral, + pFunc->iOBTab, pOBList->nExpr+nExtra, 0, + (char*)pKeyInfo, P4_KEYINFO); + ExplainQueryPlan((pParse, 0, "USE TEMP B-TREE FOR %s(ORDER BY)", + pFunc->pFunc->zName)); + } } } @@ -147037,13 +148430,56 @@ static void finalizeAggFunctions(Parse *pParse, AggInfo *pAggInfo){ ExprList *pList; assert( ExprUseXList(pF->pFExpr) ); pList = pF->pFExpr->x.pList; + if( pF->iOBTab>=0 ){ + /* For an ORDER BY aggregate, calls to OP_AggStep were deferred. Inputs + ** were stored in emphermal table pF->iOBTab. Here, we extract those + ** inputs (in ORDER BY order) and make all calls to OP_AggStep + ** before doing the OP_AggFinal call. */ + int iTop; /* Start of loop for extracting columns */ + int nArg; /* Number of columns to extract */ + int nKey; /* Key columns to be skipped */ + int regAgg; /* Extract into this array */ + int j; /* Loop counter */ + + assert( pF->pFunc!=0 ); + nArg = pList->nExpr; + regAgg = sqlite3GetTempRange(pParse, nArg); + + if( pF->bOBPayload==0 ){ + nKey = 0; + }else{ + assert( pF->pFExpr->pLeft!=0 ); + assert( ExprUseXList(pF->pFExpr->pLeft) ); + assert( pF->pFExpr->pLeft->x.pList!=0 ); + nKey = pF->pFExpr->pLeft->x.pList->nExpr; + if( ALWAYS(!pF->bOBUnique) ) nKey++; + } + iTop = sqlite3VdbeAddOp1(v, OP_Rewind, pF->iOBTab); VdbeCoverage(v); + for(j=nArg-1; j>=0; j--){ + sqlite3VdbeAddOp3(v, OP_Column, pF->iOBTab, nKey+j, regAgg+j); + } + if( pF->bUseSubtype ){ + int regSubtype = sqlite3GetTempReg(pParse); + int iBaseCol = nKey + nArg + (pF->bOBPayload==0 && pF->bOBUnique==0); + for(j=nArg-1; j>=0; j--){ + sqlite3VdbeAddOp3(v, OP_Column, pF->iOBTab, iBaseCol+j, regSubtype); + sqlite3VdbeAddOp2(v, OP_SetSubtype, regSubtype, regAgg+j); + } + sqlite3ReleaseTempReg(pParse, regSubtype); + } + sqlite3VdbeAddOp3(v, OP_AggStep, 0, regAgg, AggInfoFuncReg(pAggInfo,i)); + sqlite3VdbeAppendP4(v, pF->pFunc, P4_FUNCDEF); + sqlite3VdbeChangeP5(v, (u8)nArg); + sqlite3VdbeAddOp2(v, OP_Next, pF->iOBTab, iTop+1); VdbeCoverage(v); + sqlite3VdbeJumpHere(v, iTop); + sqlite3ReleaseTempRange(pParse, regAgg, nArg); + } sqlite3VdbeAddOp2(v, OP_AggFinal, AggInfoFuncReg(pAggInfo,i), pList ? pList->nExpr : 0); sqlite3VdbeAppendP4(v, pF->pFunc, P4_FUNCDEF); } } - /* ** Generate code that will update the accumulator memory cells for an ** aggregate based on the current cursor position. @@ -147052,6 +148488,13 @@ static void finalizeAggFunctions(Parse *pParse, AggInfo *pAggInfo){ ** in pAggInfo, then only populate the pAggInfo->nAccumulator accumulator ** registers if register regAcc contains 0. The caller will take care ** of setting and clearing regAcc. +** +** For an ORDER BY aggregate, the actual accumulator memory cell update +** is deferred until after all input rows have been received, so that they +** can be run in the requested order. In that case, instead of invoking +** OP_AggStep to update the accumulator, just add the arguments that would +** have been passed into OP_AggStep into the sorting ephemeral table +** (along with the appropriate sort key). */ static void updateAccumulator( Parse *pParse, @@ -147073,9 +148516,12 @@ static void updateAccumulator( int nArg; int addrNext = 0; int regAgg; + int regAggSz = 0; + int regDistinct = 0; ExprList *pList; assert( ExprUseXList(pF->pFExpr) ); assert( !IsWindowFunc(pF->pFExpr) ); + assert( pF->pFunc!=0 ); pList = pF->pFExpr->x.pList; if( ExprHasProperty(pF->pFExpr, EP_WinFunc) ){ Expr *pFilter = pF->pFExpr->y.pWin->pFilter; @@ -147099,9 +148545,55 @@ static void updateAccumulator( addrNext = sqlite3VdbeMakeLabel(pParse); sqlite3ExprIfFalse(pParse, pFilter, addrNext, SQLITE_JUMPIFNULL); } - if( pList ){ + if( pF->iOBTab>=0 ){ + /* Instead of invoking AggStep, we must push the arguments that would + ** have been passed to AggStep onto the sorting table. */ + int jj; /* Registered used so far in building the record */ + ExprList *pOBList; /* The ORDER BY clause */ + assert( pList!=0 ); + nArg = pList->nExpr; + assert( nArg>0 ); + assert( pF->pFExpr->pLeft!=0 ); + assert( pF->pFExpr->pLeft->op==TK_ORDER ); + assert( ExprUseXList(pF->pFExpr->pLeft) ); + pOBList = pF->pFExpr->pLeft->x.pList; + assert( pOBList!=0 ); + assert( pOBList->nExpr>0 ); + regAggSz = pOBList->nExpr; + if( !pF->bOBUnique ){ + regAggSz++; /* One register for OP_Sequence */ + } + if( pF->bOBPayload ){ + regAggSz += nArg; + } + if( pF->bUseSubtype ){ + regAggSz += nArg; + } + regAggSz++; /* One extra register to hold result of MakeRecord */ + regAgg = sqlite3GetTempRange(pParse, regAggSz); + regDistinct = regAgg; + sqlite3ExprCodeExprList(pParse, pOBList, regAgg, 0, SQLITE_ECEL_DUP); + jj = pOBList->nExpr; + if( !pF->bOBUnique ){ + sqlite3VdbeAddOp2(v, OP_Sequence, pF->iOBTab, regAgg+jj); + jj++; + } + if( pF->bOBPayload ){ + regDistinct = regAgg+jj; + sqlite3ExprCodeExprList(pParse, pList, regDistinct, 0, SQLITE_ECEL_DUP); + jj += nArg; + } + if( pF->bUseSubtype ){ + int kk; + int regBase = pF->bOBPayload ? regDistinct : regAgg; + for(kk=0; kknExpr; regAgg = sqlite3GetTempRange(pParse, nArg); + regDistinct = regAgg; sqlite3ExprCodeExprList(pParse, pList, regAgg, 0, SQLITE_ECEL_DUP); }else{ nArg = 0; @@ -147112,26 +148604,37 @@ static void updateAccumulator( addrNext = sqlite3VdbeMakeLabel(pParse); } pF->iDistinct = codeDistinct(pParse, eDistinctType, - pF->iDistinct, addrNext, pList, regAgg); + pF->iDistinct, addrNext, pList, regDistinct); } - if( pF->pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL ){ - CollSeq *pColl = 0; - struct ExprList_item *pItem; - int j; - assert( pList!=0 ); /* pList!=0 if pF->pFunc has NEEDCOLL */ - for(j=0, pItem=pList->a; !pColl && jpExpr); + if( pF->iOBTab>=0 ){ + /* Insert a new record into the ORDER BY table */ + sqlite3VdbeAddOp3(v, OP_MakeRecord, regAgg, regAggSz-1, + regAgg+regAggSz-1); + sqlite3VdbeAddOp4Int(v, OP_IdxInsert, pF->iOBTab, regAgg+regAggSz-1, + regAgg, regAggSz-1); + sqlite3ReleaseTempRange(pParse, regAgg, regAggSz); + }else{ + /* Invoke the AggStep function */ + if( pF->pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL ){ + CollSeq *pColl = 0; + struct ExprList_item *pItem; + int j; + assert( pList!=0 ); /* pList!=0 if pF->pFunc has NEEDCOLL */ + for(j=0, pItem=pList->a; !pColl && jpExpr); + } + if( !pColl ){ + pColl = pParse->db->pDfltColl; + } + if( regHit==0 && pAggInfo->nAccumulator ) regHit = ++pParse->nMem; + sqlite3VdbeAddOp4(v, OP_CollSeq, regHit, 0, 0, + (char *)pColl, P4_COLLSEQ); } - if( !pColl ){ - pColl = pParse->db->pDfltColl; - } - if( regHit==0 && pAggInfo->nAccumulator ) regHit = ++pParse->nMem; - sqlite3VdbeAddOp4(v, OP_CollSeq, regHit, 0, 0, (char *)pColl, P4_COLLSEQ); + sqlite3VdbeAddOp3(v, OP_AggStep, 0, regAgg, AggInfoFuncReg(pAggInfo,i)); + sqlite3VdbeAppendP4(v, pF->pFunc, P4_FUNCDEF); + sqlite3VdbeChangeP5(v, (u8)nArg); + sqlite3ReleaseTempRange(pParse, regAgg, nArg); } - sqlite3VdbeAddOp3(v, OP_AggStep, 0, regAgg, AggInfoFuncReg(pAggInfo,i)); - sqlite3VdbeAppendP4(v, pF->pFunc, P4_FUNCDEF); - sqlite3VdbeChangeP5(v, (u8)nArg); - sqlite3ReleaseTempRange(pParse, regAgg, nArg); if( addrNext ){ sqlite3VdbeResolveLabel(v, addrNext); } @@ -147290,7 +148793,8 @@ static SrcItem *isSelfJoinView( /* ** Deallocate a single AggInfo object */ -static void agginfoFree(sqlite3 *db, AggInfo *p){ +static void agginfoFree(sqlite3 *db, void *pArg){ + AggInfo *p = (AggInfo*)pArg; sqlite3DbFree(db, p->aCol); sqlite3DbFree(db, p->aFunc); sqlite3DbFreeNN(db, p); @@ -147364,7 +148868,7 @@ static int countOfViewOptimization(Parse *pParse, Select *p){ pSub->selFlags |= SF_Aggregate; pSub->selFlags &= ~SF_Compound; pSub->nSelectRow = 0; - sqlite3ExprListDelete(db, pSub->pEList); + sqlite3ParserAddCleanup(pParse, sqlite3ExprListDeleteGeneric, pSub->pEList); pTerm = pPrior ? sqlite3ExprDup(db, pCount, 0) : pCount; pSub->pEList = sqlite3ExprListAppend(pParse, 0, pTerm); pTerm = sqlite3PExpr(pParse, TK_SELECT, 0, 0); @@ -147544,9 +149048,8 @@ SQLITE_PRIVATE int sqlite3Select( sqlite3TreeViewExprList(0, p->pOrderBy, 0, "ORDERBY"); } #endif - sqlite3ParserAddCleanup(pParse, - (void(*)(sqlite3*,void*))sqlite3ExprListDelete, - p->pOrderBy); + sqlite3ParserAddCleanup(pParse, sqlite3ExprListDeleteGeneric, + p->pOrderBy); testcase( pParse->earlyCleanup ); p->pOrderBy = 0; } @@ -147652,6 +149155,7 @@ SQLITE_PRIVATE int sqlite3Select( TREETRACE(0x1000,pParse,p, ("LEFT-JOIN simplifies to JOIN on term %d\n",i)); pItem->fg.jointype &= ~(JT_LEFT|JT_OUTER); + unsetJoinExpr(p->pWhere, pItem->iCursor, 0); } } if( pItem->fg.jointype & JT_LTORJ ){ @@ -147666,17 +149170,15 @@ SQLITE_PRIVATE int sqlite3Select( TREETRACE(0x1000,pParse,p, ("RIGHT-JOIN simplifies to JOIN on term %d\n",j)); pI2->fg.jointype &= ~(JT_RIGHT|JT_OUTER); + unsetJoinExpr(p->pWhere, pI2->iCursor, 1); } } } - for(j=pTabList->nSrc-1; j>=i; j--){ + for(j=pTabList->nSrc-1; j>=0; j--){ pTabList->a[j].fg.jointype &= ~JT_LTORJ; if( pTabList->a[j].fg.jointype & JT_RIGHT ) break; } } - assert( pItem->iCursor>=0 ); - unsetJoinExpr(p->pWhere, pItem->iCursor, - pTabList->a[0].fg.jointype & JT_LTORJ); } /* No further action if this term of the FROM clause is not a subquery */ @@ -147739,9 +149241,8 @@ SQLITE_PRIVATE int sqlite3Select( ){ TREETRACE(0x800,pParse,p, ("omit superfluous ORDER BY on %r FROM-clause subquery\n",i+1)); - sqlite3ParserAddCleanup(pParse, - (void(*)(sqlite3*,void*))sqlite3ExprListDelete, - pSub->pOrderBy); + sqlite3ParserAddCleanup(pParse, sqlite3ExprListDeleteGeneric, + pSub->pOrderBy); pSub->pOrderBy = 0; } @@ -148270,8 +149771,7 @@ SQLITE_PRIVATE int sqlite3Select( */ pAggInfo = sqlite3DbMallocZero(db, sizeof(*pAggInfo) ); if( pAggInfo ){ - sqlite3ParserAddCleanup(pParse, - (void(*)(sqlite3*,void*))agginfoFree, pAggInfo); + sqlite3ParserAddCleanup(pParse, agginfoFree, pAggInfo); testcase( pParse->earlyCleanup ); } if( db->mallocFailed ){ @@ -149192,6 +150692,10 @@ SQLITE_PRIVATE void sqlite3BeginTrigger( sqlite3ErrorMsg(pParse, "cannot create triggers on virtual tables"); goto trigger_orphan_error; } + if( (pTab->tabFlags & TF_Shadow)!=0 && sqlite3ReadOnlyShadowTables(db) ){ + sqlite3ErrorMsg(pParse, "cannot create triggers on shadow tables"); + goto trigger_orphan_error; + } /* Check that the trigger name is not reserved and that no trigger of the ** specified name exists */ @@ -149975,10 +151479,17 @@ static void codeReturningTrigger( SrcList sFrom; assert( v!=0 ); - assert( pParse->bReturning ); + if( !pParse->bReturning ){ + /* This RETURNING trigger must be for a different statement as + ** this statement lacks a RETURNING clause. */ + return; + } assert( db->pParse==pParse ); pReturning = pParse->u1.pReturning; - assert( pTrigger == &(pReturning->retTrig) ); + if( pTrigger != &(pReturning->retTrig) ){ + /* This RETURNING trigger is for a different statement */ + return; + } memset(&sSelect, 0, sizeof(sSelect)); memset(&sFrom, 0, sizeof(sFrom)); sSelect.pEList = sqlite3ExprListDup(db, pReturning->pReturnEL, 0); @@ -152909,7 +154420,6 @@ SQLITE_PRIVATE void sqlite3VtabUnlockList(sqlite3 *db){ if( p ){ db->pDisconnect = 0; - sqlite3ExpirePreparedStatements(db, 0); do { VTable *pNext = p->pNext; sqlite3VtabUnlock(p); @@ -153415,7 +154925,7 @@ SQLITE_API int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){ sqlite3_mutex_enter(db->mutex); pCtx = db->pVtabCtx; if( !pCtx || pCtx->bDeclared ){ - sqlite3Error(db, SQLITE_MISUSE); + sqlite3Error(db, SQLITE_MISUSE_BKPT); sqlite3_mutex_leave(db->mutex); return SQLITE_MISUSE_BKPT; } @@ -154475,7 +155985,7 @@ SQLITE_PRIVATE Bitmask sqlite3WhereGetMask(WhereMaskSet*,int); #ifdef WHERETRACE_ENABLED SQLITE_PRIVATE void sqlite3WhereClausePrint(WhereClause *pWC); SQLITE_PRIVATE void sqlite3WhereTermPrint(WhereTerm *pTerm, int iTerm); -SQLITE_PRIVATE void sqlite3WhereLoopPrint(WhereLoop *p, WhereClause *pWC); +SQLITE_PRIVATE void sqlite3WhereLoopPrint(const WhereLoop *p, const WhereClause *pWC); #endif SQLITE_PRIVATE WhereTerm *sqlite3WhereFindTerm( WhereClause *pWC, /* The WHERE clause to be searched */ @@ -154606,7 +156116,7 @@ SQLITE_PRIVATE void sqlite3WhereTabFuncArgs(Parse*, SrcItem*, WhereClause*); #define WHERE_BLOOMFILTER 0x00400000 /* Consider using a Bloom-filter */ #define WHERE_SELFCULL 0x00800000 /* nOut reduced by extra WHERE terms */ #define WHERE_OMIT_OFFSET 0x01000000 /* Set offset counter to zero */ -#define WHERE_VIEWSCAN 0x02000000 /* A full-scan of a VIEW or subquery */ + /* 0x02000000 -- available for reuse */ #define WHERE_EXPRIDX 0x04000000 /* Uses an index-on-expressions */ #endif /* !defined(SQLITE_WHEREINT_H) */ @@ -159937,12 +161447,22 @@ static void translateColumnToCopy( for(; iStartp1!=iTabCur ) continue; if( pOp->opcode==OP_Column ){ +#ifdef SQLITE_DEBUG + if( pParse->db->flags & SQLITE_VdbeAddopTrace ){ + printf("TRANSLATE OP_Column to OP_Copy at %d\n", iStart); + } +#endif pOp->opcode = OP_Copy; pOp->p1 = pOp->p2 + iRegister; pOp->p2 = pOp->p3; pOp->p3 = 0; pOp->p5 = 2; /* Cause the MEM_Subtype flag to be cleared */ }else if( pOp->opcode==OP_Rowid ){ +#ifdef SQLITE_DEBUG + if( pParse->db->flags & SQLITE_VdbeAddopTrace ){ + printf("TRANSLATE OP_Rowid to OP_Sequence at %d\n", iStart); + } +#endif pOp->opcode = OP_Sequence; pOp->p1 = iAutoidxCur; #ifdef SQLITE_ALLOW_ROWID_IN_VIEW @@ -160401,13 +161921,17 @@ static SQLITE_NOINLINE void sqlite3ConstructBloomFilter( WhereLoop *pLoop = pLevel->pWLoop; /* The loop being coded */ int iCur; /* Cursor for table getting the filter */ IndexedExpr *saved_pIdxEpr; /* saved copy of Parse.pIdxEpr */ + IndexedExpr *saved_pIdxPartExpr; /* saved copy of Parse.pIdxPartExpr */ saved_pIdxEpr = pParse->pIdxEpr; + saved_pIdxPartExpr = pParse->pIdxPartExpr; pParse->pIdxEpr = 0; + pParse->pIdxPartExpr = 0; assert( pLoop!=0 ); assert( v!=0 ); assert( pLoop->wsFlags & WHERE_BLOOMFILTER ); + assert( (pLoop->wsFlags & WHERE_IDX_ONLY)==0 ); addrOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v); do{ @@ -160497,6 +162021,7 @@ static SQLITE_NOINLINE void sqlite3ConstructBloomFilter( }while( iLevel < pWInfo->nLevel ); sqlite3VdbeJumpHere(v, addrOnce); pParse->pIdxEpr = saved_pIdxEpr; + pParse->pIdxPartExpr = saved_pIdxPartExpr; } @@ -161264,7 +162789,8 @@ static int whereRangeScanEst( ** sample, then assume they are 4x more selective. This brings ** the estimated selectivity more in line with what it would be ** if estimated without the use of STAT4 tables. */ - if( iLwrIdx==iUprIdx ) nNew -= 20; assert( 20==sqlite3LogEst(4) ); + if( iLwrIdx==iUprIdx ){ nNew -= 20; } + assert( 20==sqlite3LogEst(4) ); }else{ nNew = 10; assert( 10==sqlite3LogEst(2) ); } @@ -161488,17 +163014,34 @@ SQLITE_PRIVATE void sqlite3WhereClausePrint(WhereClause *pWC){ #ifdef WHERETRACE_ENABLED /* ** Print a WhereLoop object for debugging purposes +** +** Format example: +** +** .--- Position in WHERE clause rSetup, rRun, nOut ---. +** | | +** | .--- selfMask nTerm ------. | +** | | | | +** | | .-- prereq Idx wsFlags----. | | +** | | | Name | | | +** | | | __|__ nEq ---. ___|__ | __|__ +** | / \ / \ / \ | / \ / \ / \ +** 1.002.001 t2.t2xy 2 f 010241 N 2 cost 0,56,31 */ -SQLITE_PRIVATE void sqlite3WhereLoopPrint(WhereLoop *p, WhereClause *pWC){ - WhereInfo *pWInfo = pWC->pWInfo; - int nb = 1+(pWInfo->pTabList->nSrc+3)/4; - SrcItem *pItem = pWInfo->pTabList->a + p->iTab; - Table *pTab = pItem->pTab; - Bitmask mAll = (((Bitmask)1)<<(nb*4)) - 1; - sqlite3DebugPrintf("%c%2d.%0*llx.%0*llx", p->cId, - p->iTab, nb, p->maskSelf, nb, p->prereq & mAll); - sqlite3DebugPrintf(" %12s", - pItem->zAlias ? pItem->zAlias : pTab->zName); +SQLITE_PRIVATE void sqlite3WhereLoopPrint(const WhereLoop *p, const WhereClause *pWC){ + if( pWC ){ + WhereInfo *pWInfo = pWC->pWInfo; + int nb = 1+(pWInfo->pTabList->nSrc+3)/4; + SrcItem *pItem = pWInfo->pTabList->a + p->iTab; + Table *pTab = pItem->pTab; + Bitmask mAll = (((Bitmask)1)<<(nb*4)) - 1; + sqlite3DebugPrintf("%c%2d.%0*llx.%0*llx", p->cId, + p->iTab, nb, p->maskSelf, nb, p->prereq & mAll); + sqlite3DebugPrintf(" %12s", + pItem->zAlias ? pItem->zAlias : pTab->zName); + }else{ + sqlite3DebugPrintf("%c%2d.%03llx.%03llx %c%d", + p->cId, p->iTab, p->maskSelf, p->prereq & 0xfff, p->cId, p->iTab); + } if( (p->wsFlags & WHERE_VIRTUALTABLE)==0 ){ const char *zName; if( p->u.btree.pIndex && (zName = p->u.btree.pIndex->zName)!=0 ){ @@ -161535,6 +163078,15 @@ SQLITE_PRIVATE void sqlite3WhereLoopPrint(WhereLoop *p, WhereClause *pWC){ } } } +SQLITE_PRIVATE void sqlite3ShowWhereLoop(const WhereLoop *p){ + if( p ) sqlite3WhereLoopPrint(p, 0); +} +SQLITE_PRIVATE void sqlite3ShowWhereLoopList(const WhereLoop *p){ + while( p ){ + sqlite3ShowWhereLoop(p); + p = p->pNextLoop; + } +} #endif /* @@ -161647,46 +163199,60 @@ static void whereInfoFree(sqlite3 *db, WhereInfo *pWInfo){ } /* -** Return TRUE if all of the following are true: +** Return TRUE if X is a proper subset of Y but is of equal or less cost. +** In other words, return true if all constraints of X are also part of Y +** and Y has additional constraints that might speed the search that X lacks +** but the cost of running X is not more than the cost of running Y. ** -** (1) X has the same or lower cost, or returns the same or fewer rows, -** than Y. -** (2) X uses fewer WHERE clause terms than Y -** (3) Every WHERE clause term used by X is also used by Y -** (4) X skips at least as many columns as Y -** (5) If X is a covering index, than Y is too +** In other words, return true if the cost relationwship between X and Y +** is inverted and needs to be adjusted. ** -** Conditions (2) and (3) mean that X is a "proper subset" of Y. -** If X is a proper subset of Y then Y is a better choice and ought -** to have a lower cost. This routine returns TRUE when that cost -** relationship is inverted and needs to be adjusted. Constraint (4) -** was added because if X uses skip-scan less than Y it still might -** deserve a lower cost even if it is a proper subset of Y. Constraint (5) -** was added because a covering index probably deserves to have a lower cost -** than a non-covering index even if it is a proper subset. +** Case 1: +** +** (1a) X and Y use the same index. +** (1b) X has fewer == terms than Y +** (1c) Neither X nor Y use skip-scan +** (1d) X does not have a a greater cost than Y +** +** Case 2: +** +** (2a) X has the same or lower cost, or returns the same or fewer rows, +** than Y. +** (2b) X uses fewer WHERE clause terms than Y +** (2c) Every WHERE clause term used by X is also used by Y +** (2d) X skips at least as many columns as Y +** (2e) If X is a covering index, than Y is too */ static int whereLoopCheaperProperSubset( const WhereLoop *pX, /* First WhereLoop to compare */ const WhereLoop *pY /* Compare against this WhereLoop */ ){ int i, j; - if( pX->nLTerm-pX->nSkip >= pY->nLTerm-pY->nSkip ){ - return 0; /* X is not a subset of Y */ + if( pX->rRun>pY->rRun && pX->nOut>pY->nOut ) return 0; /* (1d) and (2a) */ + assert( (pX->wsFlags & WHERE_VIRTUALTABLE)==0 ); + assert( (pY->wsFlags & WHERE_VIRTUALTABLE)==0 ); + if( pX->u.btree.nEq < pY->u.btree.nEq /* (1b) */ + && pX->u.btree.pIndex==pY->u.btree.pIndex /* (1a) */ + && pX->nSkip==0 && pY->nSkip==0 /* (1c) */ + ){ + return 1; /* Case 1 is true */ } - if( pX->rRun>pY->rRun && pX->nOut>pY->nOut ) return 0; - if( pY->nSkip > pX->nSkip ) return 0; + if( pX->nLTerm-pX->nSkip >= pY->nLTerm-pY->nSkip ){ + return 0; /* (2b) */ + } + if( pY->nSkip > pX->nSkip ) return 0; /* (2d) */ for(i=pX->nLTerm-1; i>=0; i--){ if( pX->aLTerm[i]==0 ) continue; for(j=pY->nLTerm-1; j>=0; j--){ if( pY->aLTerm[j]==pX->aLTerm[i] ) break; } - if( j<0 ) return 0; /* X not a subset of Y since term X[i] not used by Y */ + if( j<0 ) return 0; /* (2c) */ } if( (pX->wsFlags&WHERE_IDX_ONLY)!=0 && (pY->wsFlags&WHERE_IDX_ONLY)==0 ){ - return 0; /* Constraint (5) */ + return 0; /* (2e) */ } - return 1; /* All conditions meet */ + return 1; /* Case 2 is true */ } /* @@ -162176,7 +163742,10 @@ static int whereLoopAddBtreeIndex( assert( pNew->u.btree.nBtm==0 ); opMask = WO_EQ|WO_IN|WO_GT|WO_GE|WO_LT|WO_LE|WO_ISNULL|WO_IS; } - if( pProbe->bUnordered ) opMask &= ~(WO_GT|WO_GE|WO_LT|WO_LE); + if( pProbe->bUnordered || pProbe->bLowQual ){ + if( pProbe->bUnordered ) opMask &= ~(WO_GT|WO_GE|WO_LT|WO_LE); + if( pProbe->bLowQual ) opMask &= ~(WO_EQ|WO_IN|WO_IS); + } assert( pNew->u.btree.nEqnColumn ); assert( pNew->u.btree.nEqnKeyCol @@ -162756,6 +164325,100 @@ static SQLITE_NOINLINE u32 whereIsCoveringIndex( return rc; } +/* +** This is an sqlite3ParserAddCleanup() callback that is invoked to +** free the Parse->pIdxEpr list when the Parse object is destroyed. +*/ +static void whereIndexedExprCleanup(sqlite3 *db, void *pObject){ + IndexedExpr **pp = (IndexedExpr**)pObject; + while( *pp!=0 ){ + IndexedExpr *p = *pp; + *pp = p->pIENext; + sqlite3ExprDelete(db, p->pExpr); + sqlite3DbFreeNN(db, p); + } +} + +/* +** This function is called for a partial index - one with a WHERE clause - in +** two scenarios. In both cases, it determines whether or not the WHERE +** clause on the index implies that a column of the table may be safely +** replaced by a constant expression. For example, in the following +** SELECT: +** +** CREATE INDEX i1 ON t1(b, c) WHERE a=; +** SELECT a, b, c FROM t1 WHERE a= AND b=?; +** +** The "a" in the select-list may be replaced by , iff: +** +** (a) is a constant expression, and +** (b) The (a=) comparison uses the BINARY collation sequence, and +** (c) Column "a" has an affinity other than NONE or BLOB. +** +** If argument pItem is NULL, then pMask must not be NULL. In this case this +** function is being called as part of determining whether or not pIdx +** is a covering index. This function clears any bits in (*pMask) +** corresponding to columns that may be replaced by constants as described +** above. +** +** Otherwise, if pItem is not NULL, then this function is being called +** as part of coding a loop that uses index pIdx. In this case, add entries +** to the Parse.pIdxPartExpr list for each column that can be replaced +** by a constant. +*/ +static void wherePartIdxExpr( + Parse *pParse, /* Parse context */ + Index *pIdx, /* Partial index being processed */ + Expr *pPart, /* WHERE clause being processed */ + Bitmask *pMask, /* Mask to clear bits in */ + int iIdxCur, /* Cursor number for index */ + SrcItem *pItem /* The FROM clause entry for the table */ +){ + assert( pItem==0 || (pItem->fg.jointype & JT_RIGHT)==0 ); + assert( (pItem==0 || pMask==0) && (pMask!=0 || pItem!=0) ); + + if( pPart->op==TK_AND ){ + wherePartIdxExpr(pParse, pIdx, pPart->pRight, pMask, iIdxCur, pItem); + pPart = pPart->pLeft; + } + + if( (pPart->op==TK_EQ || pPart->op==TK_IS) ){ + Expr *pLeft = pPart->pLeft; + Expr *pRight = pPart->pRight; + u8 aff; + + if( pLeft->op!=TK_COLUMN ) return; + if( !sqlite3ExprIsConstant(pRight) ) return; + if( !sqlite3IsBinary(sqlite3ExprCompareCollSeq(pParse, pPart)) ) return; + if( pLeft->iColumn<0 ) return; + aff = pIdx->pTable->aCol[pLeft->iColumn].affinity; + if( aff>=SQLITE_AFF_TEXT ){ + if( pItem ){ + sqlite3 *db = pParse->db; + IndexedExpr *p = (IndexedExpr*)sqlite3DbMallocRaw(db, sizeof(*p)); + if( p ){ + int bNullRow = (pItem->fg.jointype&(JT_LEFT|JT_LTORJ))!=0; + p->pExpr = sqlite3ExprDup(db, pRight, 0); + p->iDataCur = pItem->iCursor; + p->iIdxCur = iIdxCur; + p->iIdxCol = pLeft->iColumn; + p->bMaybeNullRow = bNullRow; + p->pIENext = pParse->pIdxPartExpr; + p->aff = aff; + pParse->pIdxPartExpr = p; + if( p->pIENext==0 ){ + void *pArg = (void*)&pParse->pIdxPartExpr; + sqlite3ParserAddCleanup(pParse, whereIndexedExprCleanup, pArg); + } + } + }else if( pLeft->iColumn<(BMS-1) ){ + *pMask &= ~((Bitmask)1 << pLeft->iColumn); + } + } + } +} + + /* ** Add all WhereLoop objects for a single table of the join where the table ** is identified by pBuilder->pNew->iTab. That table is guaranteed to be @@ -162959,9 +164622,6 @@ static int whereLoopAddBtree( #else pNew->rRun = rSize + 16; #endif - if( IsView(pTab) || (pTab->tabFlags & TF_Ephemeral)!=0 ){ - pNew->wsFlags |= WHERE_VIEWSCAN; - } ApplyCostMultiplier(pNew->rRun, pTab->costMult); whereLoopOutputAdjust(pWC, pNew, rSize); rc = whereLoopInsert(pBuilder, pNew); @@ -162974,6 +164634,11 @@ static int whereLoopAddBtree( pNew->wsFlags = WHERE_IDX_ONLY | WHERE_INDEXED; }else{ m = pSrc->colUsed & pProbe->colNotIdxed; + if( pProbe->pPartIdxWhere ){ + wherePartIdxExpr( + pWInfo->pParse, pProbe, pProbe->pPartIdxWhere, &m, 0, 0 + ); + } pNew->wsFlags = WHERE_INDEXED; if( m==TOPBIT || (pProbe->bHasExpr && !pProbe->bHasVCol && m!=0) ){ u32 isCov = whereIsCoveringIndex(pWInfo, pProbe, pSrc->iCursor); @@ -163356,7 +165021,7 @@ SQLITE_API int sqlite3_vtab_rhs_value( sqlite3_value *pVal = 0; int rc = SQLITE_OK; if( iCons<0 || iCons>=pIdxInfo->nConstraint ){ - rc = SQLITE_MISUSE; /* EV: R-30545-25046 */ + rc = SQLITE_MISUSE_BKPT; /* EV: R-30545-25046 */ }else{ if( pH->aRhs[iCons]==0 ){ WhereTerm *pTerm = &pH->pWC->a[pIdxInfo->aConstraint[iCons].iTermOffset]; @@ -164380,14 +166045,6 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ rUnsorted -= 2; /* TUNING: Slight bias in favor of no-sort plans */ } - /* TUNING: A full-scan of a VIEW or subquery in the outer loop - ** is not so bad. */ - if( iLoop==0 && (pWLoop->wsFlags & WHERE_VIEWSCAN)!=0 && nLoop>1 ){ - rCost += -10; - nOut += -30; - WHERETRACE(0x80,("VIEWSCAN cost reduction for %c\n",pWLoop->cId)); - } - /* Check to see if pWLoop should be added to the set of ** mxChoice best-so-far paths. ** @@ -164937,20 +166594,6 @@ static SQLITE_NOINLINE void whereCheckIfBloomFilterIsUseful( } } -/* -** This is an sqlite3ParserAddCleanup() callback that is invoked to -** free the Parse->pIdxEpr list when the Parse object is destroyed. -*/ -static void whereIndexedExprCleanup(sqlite3 *db, void *pObject){ - Parse *pParse = (Parse*)pObject; - while( pParse->pIdxEpr!=0 ){ - IndexedExpr *p = pParse->pIdxEpr; - pParse->pIdxEpr = p->pIENext; - sqlite3ExprDelete(db, p->pExpr); - sqlite3DbFreeNN(db, p); - } -} - /* ** The index pIdx is used by a query and contains one or more expressions. ** In other words pIdx is an index on an expression. iIdxCur is the cursor @@ -164990,6 +166633,20 @@ static SQLITE_NOINLINE void whereAddIndexedExpr( continue; } if( sqlite3ExprIsConstant(pExpr) ) continue; + if( pExpr->op==TK_FUNCTION ){ + /* Functions that might set a subtype should not be replaced by the + ** value taken from an expression index since the index omits the + ** subtype. https://sqlite.org/forum/forumpost/68d284c86b082c3e */ + int n; + FuncDef *pDef; + sqlite3 *db = pParse->db; + assert( ExprUseXList(pExpr) ); + n = pExpr->x.pList ? pExpr->x.pList->nExpr : 0; + pDef = sqlite3FindFunction(db, pExpr->u.zToken, n, ENC(db), 0); + if( pDef==0 || (pDef->funcFlags & SQLITE_RESULT_SUBTYPE)!=0 ){ + continue; + } + } p = sqlite3DbMallocRaw(pParse->db, sizeof(IndexedExpr)); if( p==0 ) break; p->pIENext = pParse->pIdxEpr; @@ -165012,7 +166669,8 @@ static SQLITE_NOINLINE void whereAddIndexedExpr( #endif pParse->pIdxEpr = p; if( p->pIENext==0 ){ - sqlite3ParserAddCleanup(pParse, whereIndexedExprCleanup, pParse); + void *pArg = (void*)&pParse->pIdxEpr; + sqlite3ParserAddCleanup(pParse, whereIndexedExprCleanup, pArg); } } } @@ -165167,7 +166825,10 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( /* An ORDER/GROUP BY clause of more than 63 terms cannot be optimized */ testcase( pOrderBy && pOrderBy->nExpr==BMS-1 ); - if( pOrderBy && pOrderBy->nExpr>=BMS ) pOrderBy = 0; + if( pOrderBy && pOrderBy->nExpr>=BMS ){ + pOrderBy = 0; + wctrlFlags &= ~WHERE_WANT_DISTINCT; + } /* The number of tables in the FROM clause is limited by the number of ** bits in a Bitmask @@ -165192,7 +166853,10 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( ** field (type Bitmask) it must be aligned on an 8-byte boundary on ** some architectures. Hence the ROUND8() below. */ - nByteWInfo = ROUND8P(sizeof(WhereInfo)+(nTabList-1)*sizeof(WhereLevel)); + nByteWInfo = ROUND8P(sizeof(WhereInfo)); + if( nTabList>1 ){ + nByteWInfo = ROUND8P(nByteWInfo + (nTabList-1)*sizeof(WhereLevel)); + } pWInfo = sqlite3DbMallocRawNN(db, nByteWInfo + sizeof(WhereLoop)); if( db->mallocFailed ){ sqlite3DbFree(db, pWInfo); @@ -165402,6 +167066,16 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( wherePathSolver(pWInfo, pWInfo->nRowOut+1); if( db->mallocFailed ) goto whereBeginError; } + + /* TUNING: Assume that a DISTINCT clause on a subquery reduces + ** the output size by a factor of 8 (LogEst -30). + */ + if( (pWInfo->wctrlFlags & WHERE_WANT_DISTINCT)!=0 ){ + WHERETRACE(0x0080,("nRowOut reduced from %d to %d due to DISTINCT\n", + pWInfo->nRowOut, pWInfo->nRowOut-30)); + pWInfo->nRowOut -= 30; + } + } assert( pWInfo->pTabList!=0 ); if( pWInfo->pOrderBy==0 && (db->flags & SQLITE_ReverseOrder)!=0 ){ @@ -165614,6 +167288,11 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( if( pIx->bHasExpr && OptimizationEnabled(db, SQLITE_IndexedExpr) ){ whereAddIndexedExpr(pParse, pIx, iIndexCur, pTabItem); } + if( pIx->pPartIdxWhere && (pTabItem->fg.jointype & JT_RIGHT)==0 ){ + wherePartIdxExpr( + pParse, pIx, pIx->pPartIdxWhere, 0, iIndexCur, pTabItem + ); + } } pLevel->iIdxCur = iIndexCur; assert( pIx!=0 ); @@ -165739,6 +167418,11 @@ whereBeginError: pParse->nQueryLoop = pWInfo->savedNQueryLoop; whereInfoFree(db, pWInfo); } +#ifdef WHERETRACE_ENABLED + /* Prevent harmless compiler warnings about debugging routines + ** being declared but never used */ + sqlite3ShowWhereLoopList(0); +#endif /* WHERETRACE_ENABLED */ return 0; } @@ -167156,7 +168840,7 @@ SQLITE_PRIVATE int sqlite3WindowRewrite(Parse *pParse, Select *p){ assert( ExprUseXList(pWin->pOwner) ); assert( pWin->pWFunc!=0 ); pArgs = pWin->pOwner->x.pList; - if( pWin->pWFunc->funcFlags & SQLITE_FUNC_SUBTYPE ){ + if( pWin->pWFunc->funcFlags & SQLITE_SUBTYPE ){ selectWindowRewriteEList(pParse, pMWin, pSrc, pArgs, pTab, &pSublist); pWin->iArgCol = (pSublist ? pSublist->nExpr : 0); pWin->bExprArgs = 1; @@ -167430,8 +169114,9 @@ SQLITE_PRIVATE void sqlite3WindowAttach(Parse *pParse, Expr *p, Window *pWin){ if( p ){ assert( p->op==TK_FUNCTION ); assert( pWin ); + assert( ExprIsFullSize(p) ); p->y.pWin = pWin; - ExprSetProperty(p, EP_WinFunc); + ExprSetProperty(p, EP_WinFunc|EP_FullSize); pWin->pOwner = p; if( (p->flags & EP_Distinct) && pWin->eFrmType!=TK_FILTER ){ sqlite3ErrorMsg(pParse, @@ -169733,18 +171418,18 @@ typedef union { #define sqlite3ParserCTX_FETCH Parse *pParse=yypParser->pParse; #define sqlite3ParserCTX_STORE yypParser->pParse=pParse; #define YYFALLBACK 1 -#define YYNSTATE 575 -#define YYNRULE 403 -#define YYNRULE_WITH_ACTION 338 +#define YYNSTATE 579 +#define YYNRULE 405 +#define YYNRULE_WITH_ACTION 340 #define YYNTOKEN 185 -#define YY_MAX_SHIFT 574 -#define YY_MIN_SHIFTREDUCE 833 -#define YY_MAX_SHIFTREDUCE 1235 -#define YY_ERROR_ACTION 1236 -#define YY_ACCEPT_ACTION 1237 -#define YY_NO_ACTION 1238 -#define YY_MIN_REDUCE 1239 -#define YY_MAX_REDUCE 1641 +#define YY_MAX_SHIFT 578 +#define YY_MIN_SHIFTREDUCE 838 +#define YY_MAX_SHIFTREDUCE 1242 +#define YY_ERROR_ACTION 1243 +#define YY_ACCEPT_ACTION 1244 +#define YY_NO_ACTION 1245 +#define YY_MIN_REDUCE 1246 +#define YY_MAX_REDUCE 1650 /************* End control #defines *******************************************/ #define YY_NLOOKAHEAD ((int)(sizeof(yy_lookahead)/sizeof(yy_lookahead[0]))) @@ -169811,218 +171496,218 @@ typedef union { ** yy_default[] Default action for each state. ** *********** Begin parsing tables **********************************************/ -#define YY_ACTTAB_COUNT (2096) +#define YY_ACTTAB_COUNT (2100) static const YYACTIONTYPE yy_action[] = { - /* 0 */ 568, 208, 568, 118, 115, 229, 568, 118, 115, 229, - /* 10 */ 568, 1310, 377, 1289, 408, 562, 562, 562, 568, 409, - /* 20 */ 378, 1310, 1272, 41, 41, 41, 41, 208, 1520, 71, - /* 30 */ 71, 969, 419, 41, 41, 491, 303, 279, 303, 970, - /* 40 */ 397, 71, 71, 125, 126, 80, 1210, 1210, 1047, 1050, - /* 50 */ 1037, 1037, 123, 123, 124, 124, 124, 124, 476, 409, - /* 60 */ 1237, 1, 1, 574, 2, 1241, 550, 118, 115, 229, - /* 70 */ 317, 480, 146, 480, 524, 118, 115, 229, 529, 1323, - /* 80 */ 417, 523, 142, 125, 126, 80, 1210, 1210, 1047, 1050, - /* 90 */ 1037, 1037, 123, 123, 124, 124, 124, 124, 118, 115, - /* 100 */ 229, 327, 122, 122, 122, 122, 121, 121, 120, 120, - /* 110 */ 120, 119, 116, 444, 284, 284, 284, 284, 442, 442, - /* 120 */ 442, 1559, 376, 1561, 1186, 375, 1157, 565, 1157, 565, - /* 130 */ 409, 1559, 537, 259, 226, 444, 101, 145, 449, 316, - /* 140 */ 559, 240, 122, 122, 122, 122, 121, 121, 120, 120, - /* 150 */ 120, 119, 116, 444, 125, 126, 80, 1210, 1210, 1047, - /* 160 */ 1050, 1037, 1037, 123, 123, 124, 124, 124, 124, 142, - /* 170 */ 294, 1186, 339, 448, 120, 120, 120, 119, 116, 444, - /* 180 */ 127, 1186, 1187, 1186, 148, 441, 440, 568, 119, 116, - /* 190 */ 444, 124, 124, 124, 124, 117, 122, 122, 122, 122, - /* 200 */ 121, 121, 120, 120, 120, 119, 116, 444, 454, 113, - /* 210 */ 13, 13, 546, 122, 122, 122, 122, 121, 121, 120, - /* 220 */ 120, 120, 119, 116, 444, 422, 316, 559, 1186, 1187, - /* 230 */ 1186, 149, 1218, 409, 1218, 124, 124, 124, 124, 122, - /* 240 */ 122, 122, 122, 121, 121, 120, 120, 120, 119, 116, - /* 250 */ 444, 465, 342, 1034, 1034, 1048, 1051, 125, 126, 80, - /* 260 */ 1210, 1210, 1047, 1050, 1037, 1037, 123, 123, 124, 124, - /* 270 */ 124, 124, 1275, 522, 222, 1186, 568, 409, 224, 514, - /* 280 */ 175, 82, 83, 122, 122, 122, 122, 121, 121, 120, - /* 290 */ 120, 120, 119, 116, 444, 1005, 16, 16, 1186, 133, - /* 300 */ 133, 125, 126, 80, 1210, 1210, 1047, 1050, 1037, 1037, - /* 310 */ 123, 123, 124, 124, 124, 124, 122, 122, 122, 122, - /* 320 */ 121, 121, 120, 120, 120, 119, 116, 444, 1038, 546, - /* 330 */ 1186, 373, 1186, 1187, 1186, 252, 1429, 399, 504, 501, - /* 340 */ 500, 111, 560, 566, 4, 924, 924, 433, 499, 340, - /* 350 */ 460, 328, 360, 394, 1231, 1186, 1187, 1186, 563, 568, - /* 360 */ 122, 122, 122, 122, 121, 121, 120, 120, 120, 119, - /* 370 */ 116, 444, 284, 284, 369, 1572, 1598, 441, 440, 154, - /* 380 */ 409, 445, 71, 71, 1282, 565, 1215, 1186, 1187, 1186, - /* 390 */ 85, 1217, 271, 557, 543, 515, 515, 568, 98, 1216, - /* 400 */ 6, 1274, 472, 142, 125, 126, 80, 1210, 1210, 1047, - /* 410 */ 1050, 1037, 1037, 123, 123, 124, 124, 124, 124, 550, - /* 420 */ 13, 13, 1024, 507, 1218, 1186, 1218, 549, 109, 109, - /* 430 */ 222, 568, 1232, 175, 568, 427, 110, 197, 445, 569, - /* 440 */ 445, 430, 1546, 1014, 325, 551, 1186, 270, 287, 368, - /* 450 */ 510, 363, 509, 257, 71, 71, 543, 71, 71, 359, - /* 460 */ 316, 559, 1604, 122, 122, 122, 122, 121, 121, 120, - /* 470 */ 120, 120, 119, 116, 444, 1014, 1014, 1016, 1017, 27, - /* 480 */ 284, 284, 1186, 1187, 1186, 1152, 568, 1603, 409, 899, - /* 490 */ 190, 550, 356, 565, 550, 935, 533, 517, 1152, 516, - /* 500 */ 413, 1152, 552, 1186, 1187, 1186, 568, 544, 544, 51, - /* 510 */ 51, 214, 125, 126, 80, 1210, 1210, 1047, 1050, 1037, - /* 520 */ 1037, 123, 123, 124, 124, 124, 124, 1186, 474, 135, - /* 530 */ 135, 409, 284, 284, 1484, 505, 121, 121, 120, 120, - /* 540 */ 120, 119, 116, 444, 1005, 565, 518, 217, 541, 541, - /* 550 */ 316, 559, 142, 6, 532, 125, 126, 80, 1210, 1210, - /* 560 */ 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, 124, - /* 570 */ 1548, 122, 122, 122, 122, 121, 121, 120, 120, 120, - /* 580 */ 119, 116, 444, 485, 1186, 1187, 1186, 482, 281, 1263, - /* 590 */ 955, 252, 1186, 373, 504, 501, 500, 1186, 340, 570, - /* 600 */ 1186, 570, 409, 292, 499, 955, 874, 191, 480, 316, - /* 610 */ 559, 384, 290, 380, 122, 122, 122, 122, 121, 121, - /* 620 */ 120, 120, 120, 119, 116, 444, 125, 126, 80, 1210, - /* 630 */ 1210, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, - /* 640 */ 124, 409, 394, 1132, 1186, 867, 100, 284, 284, 1186, - /* 650 */ 1187, 1186, 373, 1089, 1186, 1187, 1186, 1186, 1187, 1186, - /* 660 */ 565, 455, 32, 373, 233, 125, 126, 80, 1210, 1210, - /* 670 */ 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, 124, - /* 680 */ 1428, 957, 568, 228, 956, 122, 122, 122, 122, 121, - /* 690 */ 121, 120, 120, 120, 119, 116, 444, 1152, 228, 1186, - /* 700 */ 157, 1186, 1187, 1186, 1547, 13, 13, 301, 955, 1226, - /* 710 */ 1152, 153, 409, 1152, 373, 1575, 1170, 5, 369, 1572, - /* 720 */ 429, 1232, 3, 955, 122, 122, 122, 122, 121, 121, - /* 730 */ 120, 120, 120, 119, 116, 444, 125, 126, 80, 1210, - /* 740 */ 1210, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, - /* 750 */ 124, 409, 208, 567, 1186, 1025, 1186, 1187, 1186, 1186, - /* 760 */ 388, 850, 155, 1546, 286, 402, 1094, 1094, 488, 568, - /* 770 */ 465, 342, 1315, 1315, 1546, 125, 126, 80, 1210, 1210, - /* 780 */ 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, 124, - /* 790 */ 129, 568, 13, 13, 374, 122, 122, 122, 122, 121, - /* 800 */ 121, 120, 120, 120, 119, 116, 444, 302, 568, 453, - /* 810 */ 528, 1186, 1187, 1186, 13, 13, 1186, 1187, 1186, 1293, - /* 820 */ 463, 1263, 409, 1313, 1313, 1546, 1010, 453, 452, 200, - /* 830 */ 299, 71, 71, 1261, 122, 122, 122, 122, 121, 121, - /* 840 */ 120, 120, 120, 119, 116, 444, 125, 126, 80, 1210, - /* 850 */ 1210, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, - /* 860 */ 124, 409, 227, 1069, 1152, 284, 284, 419, 312, 278, - /* 870 */ 278, 285, 285, 1415, 406, 405, 382, 1152, 565, 568, - /* 880 */ 1152, 1189, 565, 1592, 565, 125, 126, 80, 1210, 1210, - /* 890 */ 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, 124, - /* 900 */ 453, 1476, 13, 13, 1530, 122, 122, 122, 122, 121, - /* 910 */ 121, 120, 120, 120, 119, 116, 444, 201, 568, 354, - /* 920 */ 1578, 574, 2, 1241, 838, 839, 840, 1554, 317, 1205, - /* 930 */ 146, 6, 409, 255, 254, 253, 206, 1323, 9, 1189, - /* 940 */ 262, 71, 71, 424, 122, 122, 122, 122, 121, 121, - /* 950 */ 120, 120, 120, 119, 116, 444, 125, 126, 80, 1210, - /* 960 */ 1210, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, - /* 970 */ 124, 568, 284, 284, 568, 1206, 409, 573, 313, 1241, - /* 980 */ 349, 1292, 352, 419, 317, 565, 146, 491, 525, 1635, - /* 990 */ 395, 371, 491, 1323, 70, 70, 1291, 71, 71, 240, - /* 1000 */ 1321, 104, 80, 1210, 1210, 1047, 1050, 1037, 1037, 123, - /* 1010 */ 123, 124, 124, 124, 124, 122, 122, 122, 122, 121, - /* 1020 */ 121, 120, 120, 120, 119, 116, 444, 1110, 284, 284, - /* 1030 */ 428, 448, 1519, 1206, 439, 284, 284, 1483, 1348, 311, - /* 1040 */ 474, 565, 1111, 969, 491, 491, 217, 1259, 565, 1532, - /* 1050 */ 568, 970, 207, 568, 1024, 240, 383, 1112, 519, 122, - /* 1060 */ 122, 122, 122, 121, 121, 120, 120, 120, 119, 116, - /* 1070 */ 444, 1015, 107, 71, 71, 1014, 13, 13, 910, 568, - /* 1080 */ 1489, 568, 284, 284, 97, 526, 491, 448, 911, 1322, - /* 1090 */ 1318, 545, 409, 284, 284, 565, 151, 209, 1489, 1491, - /* 1100 */ 262, 450, 55, 55, 56, 56, 565, 1014, 1014, 1016, - /* 1110 */ 443, 332, 409, 527, 12, 295, 125, 126, 80, 1210, - /* 1120 */ 1210, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, - /* 1130 */ 124, 347, 409, 862, 1528, 1206, 125, 126, 80, 1210, - /* 1140 */ 1210, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, - /* 1150 */ 124, 1133, 1633, 474, 1633, 371, 125, 114, 80, 1210, - /* 1160 */ 1210, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, - /* 1170 */ 124, 1489, 329, 474, 331, 122, 122, 122, 122, 121, - /* 1180 */ 121, 120, 120, 120, 119, 116, 444, 203, 1415, 568, - /* 1190 */ 1290, 862, 464, 1206, 436, 122, 122, 122, 122, 121, - /* 1200 */ 121, 120, 120, 120, 119, 116, 444, 553, 1133, 1634, - /* 1210 */ 539, 1634, 15, 15, 890, 122, 122, 122, 122, 121, - /* 1220 */ 121, 120, 120, 120, 119, 116, 444, 568, 298, 538, - /* 1230 */ 1131, 1415, 1552, 1553, 1327, 409, 6, 6, 1163, 1264, - /* 1240 */ 415, 320, 284, 284, 1415, 508, 565, 525, 300, 457, - /* 1250 */ 43, 43, 568, 891, 12, 565, 330, 478, 425, 407, - /* 1260 */ 126, 80, 1210, 1210, 1047, 1050, 1037, 1037, 123, 123, - /* 1270 */ 124, 124, 124, 124, 568, 57, 57, 288, 1186, 1415, - /* 1280 */ 496, 458, 392, 392, 391, 273, 389, 1131, 1551, 847, - /* 1290 */ 1163, 407, 6, 568, 321, 1152, 470, 44, 44, 1550, - /* 1300 */ 1110, 426, 234, 6, 323, 256, 540, 256, 1152, 431, - /* 1310 */ 568, 1152, 322, 17, 487, 1111, 58, 58, 122, 122, - /* 1320 */ 122, 122, 121, 121, 120, 120, 120, 119, 116, 444, - /* 1330 */ 1112, 216, 481, 59, 59, 1186, 1187, 1186, 111, 560, - /* 1340 */ 324, 4, 236, 456, 526, 568, 237, 456, 568, 437, - /* 1350 */ 168, 556, 420, 141, 479, 563, 568, 293, 568, 1091, - /* 1360 */ 568, 293, 568, 1091, 531, 568, 870, 8, 60, 60, - /* 1370 */ 235, 61, 61, 568, 414, 568, 414, 568, 445, 62, - /* 1380 */ 62, 45, 45, 46, 46, 47, 47, 199, 49, 49, - /* 1390 */ 557, 568, 359, 568, 100, 486, 50, 50, 63, 63, - /* 1400 */ 64, 64, 561, 415, 535, 410, 568, 1024, 568, 534, - /* 1410 */ 316, 559, 316, 559, 65, 65, 14, 14, 568, 1024, - /* 1420 */ 568, 512, 930, 870, 1015, 109, 109, 929, 1014, 66, - /* 1430 */ 66, 131, 131, 110, 451, 445, 569, 445, 416, 177, - /* 1440 */ 1014, 132, 132, 67, 67, 568, 467, 568, 930, 471, - /* 1450 */ 1360, 283, 226, 929, 315, 1359, 407, 568, 459, 407, - /* 1460 */ 1014, 1014, 1016, 239, 407, 86, 213, 1346, 52, 52, - /* 1470 */ 68, 68, 1014, 1014, 1016, 1017, 27, 1577, 1174, 447, - /* 1480 */ 69, 69, 288, 97, 108, 1535, 106, 392, 392, 391, - /* 1490 */ 273, 389, 568, 877, 847, 881, 568, 111, 560, 466, - /* 1500 */ 4, 568, 152, 30, 38, 568, 1128, 234, 396, 323, - /* 1510 */ 111, 560, 527, 4, 563, 53, 53, 322, 568, 163, - /* 1520 */ 163, 568, 337, 468, 164, 164, 333, 563, 76, 76, - /* 1530 */ 568, 289, 1508, 568, 31, 1507, 568, 445, 338, 483, - /* 1540 */ 100, 54, 54, 344, 72, 72, 296, 236, 1076, 557, - /* 1550 */ 445, 877, 1356, 134, 134, 168, 73, 73, 141, 161, - /* 1560 */ 161, 1566, 557, 535, 568, 319, 568, 348, 536, 1007, - /* 1570 */ 473, 261, 261, 889, 888, 235, 535, 568, 1024, 568, - /* 1580 */ 475, 534, 261, 367, 109, 109, 521, 136, 136, 130, - /* 1590 */ 130, 1024, 110, 366, 445, 569, 445, 109, 109, 1014, - /* 1600 */ 162, 162, 156, 156, 568, 110, 1076, 445, 569, 445, - /* 1610 */ 410, 351, 1014, 568, 353, 316, 559, 568, 343, 568, - /* 1620 */ 100, 497, 357, 258, 100, 896, 897, 140, 140, 355, - /* 1630 */ 1306, 1014, 1014, 1016, 1017, 27, 139, 139, 362, 451, - /* 1640 */ 137, 137, 138, 138, 1014, 1014, 1016, 1017, 27, 1174, - /* 1650 */ 447, 568, 372, 288, 111, 560, 1018, 4, 392, 392, - /* 1660 */ 391, 273, 389, 568, 1137, 847, 568, 1072, 568, 258, - /* 1670 */ 492, 563, 568, 211, 75, 75, 555, 960, 234, 261, - /* 1680 */ 323, 111, 560, 927, 4, 113, 77, 77, 322, 74, - /* 1690 */ 74, 42, 42, 1369, 445, 48, 48, 1414, 563, 972, - /* 1700 */ 973, 1088, 1087, 1088, 1087, 860, 557, 150, 928, 1342, - /* 1710 */ 113, 1354, 554, 1419, 1018, 1271, 1262, 1250, 236, 1249, - /* 1720 */ 1251, 445, 1585, 1339, 308, 276, 168, 309, 11, 141, - /* 1730 */ 393, 310, 232, 557, 1401, 1024, 335, 291, 1396, 219, - /* 1740 */ 336, 109, 109, 934, 297, 1406, 235, 341, 477, 110, - /* 1750 */ 502, 445, 569, 445, 1389, 1405, 1014, 400, 1289, 365, - /* 1760 */ 223, 1480, 1024, 1479, 1351, 1352, 1350, 1349, 109, 109, - /* 1770 */ 204, 1588, 1226, 558, 265, 218, 110, 205, 445, 569, - /* 1780 */ 445, 410, 387, 1014, 1527, 179, 316, 559, 1014, 1014, - /* 1790 */ 1016, 1017, 27, 230, 1525, 1223, 79, 560, 85, 4, - /* 1800 */ 418, 215, 548, 81, 84, 188, 1402, 173, 181, 461, - /* 1810 */ 451, 35, 462, 563, 183, 1014, 1014, 1016, 1017, 27, - /* 1820 */ 184, 1485, 185, 186, 495, 242, 98, 398, 1408, 36, - /* 1830 */ 1407, 484, 91, 469, 401, 1410, 445, 192, 1474, 246, - /* 1840 */ 1496, 490, 346, 277, 248, 196, 493, 511, 557, 350, - /* 1850 */ 1252, 249, 250, 403, 1309, 1308, 111, 560, 432, 4, - /* 1860 */ 1307, 1300, 93, 1602, 881, 1601, 224, 404, 434, 520, - /* 1870 */ 263, 435, 1571, 563, 1279, 1278, 364, 1024, 306, 1277, - /* 1880 */ 264, 1600, 1557, 109, 109, 370, 1299, 307, 1556, 438, - /* 1890 */ 128, 110, 1374, 445, 569, 445, 445, 546, 1014, 10, - /* 1900 */ 1461, 105, 381, 1373, 34, 571, 99, 1332, 557, 314, - /* 1910 */ 1180, 530, 272, 274, 379, 210, 1331, 547, 385, 386, - /* 1920 */ 275, 572, 1247, 1242, 411, 412, 1512, 165, 178, 1513, - /* 1930 */ 1014, 1014, 1016, 1017, 27, 1511, 1510, 1024, 78, 147, - /* 1940 */ 166, 220, 221, 109, 109, 834, 304, 167, 446, 212, - /* 1950 */ 318, 110, 231, 445, 569, 445, 144, 1086, 1014, 1084, - /* 1960 */ 326, 180, 169, 1205, 182, 334, 238, 913, 241, 1100, - /* 1970 */ 187, 170, 171, 421, 87, 88, 423, 189, 89, 90, - /* 1980 */ 172, 1103, 243, 1099, 244, 158, 18, 245, 345, 247, - /* 1990 */ 1014, 1014, 1016, 1017, 27, 261, 1092, 193, 1220, 489, - /* 2000 */ 194, 37, 366, 849, 494, 251, 195, 506, 92, 19, - /* 2010 */ 498, 358, 20, 503, 879, 361, 94, 892, 305, 159, - /* 2020 */ 513, 39, 95, 1168, 160, 1053, 964, 1139, 96, 174, - /* 2030 */ 1138, 225, 280, 282, 198, 958, 113, 1158, 1154, 260, - /* 2040 */ 21, 22, 23, 1156, 1162, 1161, 1143, 24, 33, 25, - /* 2050 */ 202, 542, 26, 100, 1067, 102, 1054, 103, 7, 1052, - /* 2060 */ 1056, 1109, 1057, 1108, 266, 267, 28, 40, 390, 1019, - /* 2070 */ 861, 112, 29, 564, 1176, 1175, 268, 176, 143, 923, - /* 2080 */ 1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238, - /* 2090 */ 1238, 1238, 1238, 1238, 269, 1593, + /* 0 */ 572, 210, 572, 119, 116, 231, 572, 119, 116, 231, + /* 10 */ 572, 1317, 379, 1296, 410, 566, 566, 566, 572, 411, + /* 20 */ 380, 1317, 1279, 42, 42, 42, 42, 210, 1529, 72, + /* 30 */ 72, 974, 421, 42, 42, 495, 305, 281, 305, 975, + /* 40 */ 399, 72, 72, 126, 127, 81, 1217, 1217, 1054, 1057, + /* 50 */ 1044, 1044, 124, 124, 125, 125, 125, 125, 480, 411, + /* 60 */ 1244, 1, 1, 578, 2, 1248, 554, 119, 116, 231, + /* 70 */ 319, 484, 147, 484, 528, 119, 116, 231, 533, 1330, + /* 80 */ 419, 527, 143, 126, 127, 81, 1217, 1217, 1054, 1057, + /* 90 */ 1044, 1044, 124, 124, 125, 125, 125, 125, 119, 116, + /* 100 */ 231, 329, 123, 123, 123, 123, 122, 122, 121, 121, + /* 110 */ 121, 120, 117, 448, 286, 286, 286, 286, 446, 446, + /* 120 */ 446, 1568, 378, 1570, 1193, 377, 1164, 569, 1164, 569, + /* 130 */ 411, 1568, 541, 261, 228, 448, 102, 146, 453, 318, + /* 140 */ 563, 242, 123, 123, 123, 123, 122, 122, 121, 121, + /* 150 */ 121, 120, 117, 448, 126, 127, 81, 1217, 1217, 1054, + /* 160 */ 1057, 1044, 1044, 124, 124, 125, 125, 125, 125, 143, + /* 170 */ 296, 1193, 341, 452, 121, 121, 121, 120, 117, 448, + /* 180 */ 128, 1193, 1194, 1193, 149, 445, 444, 572, 120, 117, + /* 190 */ 448, 125, 125, 125, 125, 118, 123, 123, 123, 123, + /* 200 */ 122, 122, 121, 121, 121, 120, 117, 448, 458, 114, + /* 210 */ 13, 13, 550, 123, 123, 123, 123, 122, 122, 121, + /* 220 */ 121, 121, 120, 117, 448, 424, 318, 563, 1193, 1194, + /* 230 */ 1193, 150, 1225, 411, 1225, 125, 125, 125, 125, 123, + /* 240 */ 123, 123, 123, 122, 122, 121, 121, 121, 120, 117, + /* 250 */ 448, 469, 344, 1041, 1041, 1055, 1058, 126, 127, 81, + /* 260 */ 1217, 1217, 1054, 1057, 1044, 1044, 124, 124, 125, 125, + /* 270 */ 125, 125, 1282, 526, 224, 1193, 572, 411, 226, 519, + /* 280 */ 177, 83, 84, 123, 123, 123, 123, 122, 122, 121, + /* 290 */ 121, 121, 120, 117, 448, 1010, 16, 16, 1193, 134, + /* 300 */ 134, 126, 127, 81, 1217, 1217, 1054, 1057, 1044, 1044, + /* 310 */ 124, 124, 125, 125, 125, 125, 123, 123, 123, 123, + /* 320 */ 122, 122, 121, 121, 121, 120, 117, 448, 1045, 550, + /* 330 */ 1193, 375, 1193, 1194, 1193, 254, 1438, 401, 508, 505, + /* 340 */ 504, 112, 564, 570, 4, 929, 929, 435, 503, 342, + /* 350 */ 464, 330, 362, 396, 1238, 1193, 1194, 1193, 567, 572, + /* 360 */ 123, 123, 123, 123, 122, 122, 121, 121, 121, 120, + /* 370 */ 117, 448, 286, 286, 371, 1581, 1607, 445, 444, 155, + /* 380 */ 411, 449, 72, 72, 1289, 569, 1222, 1193, 1194, 1193, + /* 390 */ 86, 1224, 273, 561, 547, 520, 520, 572, 99, 1223, + /* 400 */ 6, 1281, 476, 143, 126, 127, 81, 1217, 1217, 1054, + /* 410 */ 1057, 1044, 1044, 124, 124, 125, 125, 125, 125, 554, + /* 420 */ 13, 13, 1031, 511, 1225, 1193, 1225, 553, 110, 110, + /* 430 */ 224, 572, 1239, 177, 572, 429, 111, 199, 449, 573, + /* 440 */ 449, 432, 1555, 1019, 327, 555, 1193, 272, 289, 370, + /* 450 */ 514, 365, 513, 259, 72, 72, 547, 72, 72, 361, + /* 460 */ 318, 563, 1613, 123, 123, 123, 123, 122, 122, 121, + /* 470 */ 121, 121, 120, 117, 448, 1019, 1019, 1021, 1022, 28, + /* 480 */ 286, 286, 1193, 1194, 1193, 1159, 572, 1612, 411, 904, + /* 490 */ 192, 554, 358, 569, 554, 940, 537, 521, 1159, 437, + /* 500 */ 415, 1159, 556, 1193, 1194, 1193, 572, 548, 548, 52, + /* 510 */ 52, 216, 126, 127, 81, 1217, 1217, 1054, 1057, 1044, + /* 520 */ 1044, 124, 124, 125, 125, 125, 125, 1193, 478, 136, + /* 530 */ 136, 411, 286, 286, 1493, 509, 122, 122, 121, 121, + /* 540 */ 121, 120, 117, 448, 1010, 569, 522, 219, 545, 545, + /* 550 */ 318, 563, 143, 6, 536, 126, 127, 81, 1217, 1217, + /* 560 */ 1054, 1057, 1044, 1044, 124, 124, 125, 125, 125, 125, + /* 570 */ 1557, 123, 123, 123, 123, 122, 122, 121, 121, 121, + /* 580 */ 120, 117, 448, 489, 1193, 1194, 1193, 486, 283, 1270, + /* 590 */ 960, 254, 1193, 375, 508, 505, 504, 1193, 342, 574, + /* 600 */ 1193, 574, 411, 294, 503, 960, 879, 193, 484, 318, + /* 610 */ 563, 386, 292, 382, 123, 123, 123, 123, 122, 122, + /* 620 */ 121, 121, 121, 120, 117, 448, 126, 127, 81, 1217, + /* 630 */ 1217, 1054, 1057, 1044, 1044, 124, 124, 125, 125, 125, + /* 640 */ 125, 411, 396, 1139, 1193, 872, 101, 286, 286, 1193, + /* 650 */ 1194, 1193, 375, 1096, 1193, 1194, 1193, 1193, 1194, 1193, + /* 660 */ 569, 459, 33, 375, 235, 126, 127, 81, 1217, 1217, + /* 670 */ 1054, 1057, 1044, 1044, 124, 124, 125, 125, 125, 125, + /* 680 */ 1437, 962, 572, 230, 961, 123, 123, 123, 123, 122, + /* 690 */ 122, 121, 121, 121, 120, 117, 448, 1159, 230, 1193, + /* 700 */ 158, 1193, 1194, 1193, 1556, 13, 13, 303, 960, 1233, + /* 710 */ 1159, 154, 411, 1159, 375, 1584, 1177, 5, 371, 1581, + /* 720 */ 431, 1239, 3, 960, 123, 123, 123, 123, 122, 122, + /* 730 */ 121, 121, 121, 120, 117, 448, 126, 127, 81, 1217, + /* 740 */ 1217, 1054, 1057, 1044, 1044, 124, 124, 125, 125, 125, + /* 750 */ 125, 411, 210, 571, 1193, 1032, 1193, 1194, 1193, 1193, + /* 760 */ 390, 855, 156, 1555, 376, 404, 1101, 1101, 492, 572, + /* 770 */ 469, 344, 1322, 1322, 1555, 126, 127, 81, 1217, 1217, + /* 780 */ 1054, 1057, 1044, 1044, 124, 124, 125, 125, 125, 125, + /* 790 */ 130, 572, 13, 13, 532, 123, 123, 123, 123, 122, + /* 800 */ 122, 121, 121, 121, 120, 117, 448, 304, 572, 457, + /* 810 */ 229, 1193, 1194, 1193, 13, 13, 1193, 1194, 1193, 1300, + /* 820 */ 467, 1270, 411, 1320, 1320, 1555, 1015, 457, 456, 436, + /* 830 */ 301, 72, 72, 1268, 123, 123, 123, 123, 122, 122, + /* 840 */ 121, 121, 121, 120, 117, 448, 126, 127, 81, 1217, + /* 850 */ 1217, 1054, 1057, 1044, 1044, 124, 124, 125, 125, 125, + /* 860 */ 125, 411, 384, 1076, 1159, 286, 286, 421, 314, 280, + /* 870 */ 280, 287, 287, 461, 408, 407, 1539, 1159, 569, 572, + /* 880 */ 1159, 1196, 569, 409, 569, 126, 127, 81, 1217, 1217, + /* 890 */ 1054, 1057, 1044, 1044, 124, 124, 125, 125, 125, 125, + /* 900 */ 457, 1485, 13, 13, 1541, 123, 123, 123, 123, 122, + /* 910 */ 122, 121, 121, 121, 120, 117, 448, 202, 572, 462, + /* 920 */ 1587, 578, 2, 1248, 843, 844, 845, 1563, 319, 409, + /* 930 */ 147, 6, 411, 257, 256, 255, 208, 1330, 9, 1196, + /* 940 */ 264, 72, 72, 1436, 123, 123, 123, 123, 122, 122, + /* 950 */ 121, 121, 121, 120, 117, 448, 126, 127, 81, 1217, + /* 960 */ 1217, 1054, 1057, 1044, 1044, 124, 124, 125, 125, 125, + /* 970 */ 125, 572, 286, 286, 572, 1213, 411, 577, 315, 1248, + /* 980 */ 421, 371, 1581, 356, 319, 569, 147, 495, 529, 1644, + /* 990 */ 397, 935, 495, 1330, 71, 71, 934, 72, 72, 242, + /* 1000 */ 1328, 105, 81, 1217, 1217, 1054, 1057, 1044, 1044, 124, + /* 1010 */ 124, 125, 125, 125, 125, 123, 123, 123, 123, 122, + /* 1020 */ 122, 121, 121, 121, 120, 117, 448, 1117, 286, 286, + /* 1030 */ 1422, 452, 1528, 1213, 443, 286, 286, 1492, 1355, 313, + /* 1040 */ 478, 569, 1118, 454, 351, 495, 354, 1266, 569, 209, + /* 1050 */ 572, 418, 179, 572, 1031, 242, 385, 1119, 523, 123, + /* 1060 */ 123, 123, 123, 122, 122, 121, 121, 121, 120, 117, + /* 1070 */ 448, 1020, 108, 72, 72, 1019, 13, 13, 915, 572, + /* 1080 */ 1498, 572, 286, 286, 98, 530, 1537, 452, 916, 1334, + /* 1090 */ 1329, 203, 411, 286, 286, 569, 152, 211, 1498, 1500, + /* 1100 */ 426, 569, 56, 56, 57, 57, 569, 1019, 1019, 1021, + /* 1110 */ 447, 572, 411, 531, 12, 297, 126, 127, 81, 1217, + /* 1120 */ 1217, 1054, 1057, 1044, 1044, 124, 124, 125, 125, 125, + /* 1130 */ 125, 572, 411, 867, 15, 15, 126, 127, 81, 1217, + /* 1140 */ 1217, 1054, 1057, 1044, 1044, 124, 124, 125, 125, 125, + /* 1150 */ 125, 373, 529, 264, 44, 44, 126, 115, 81, 1217, + /* 1160 */ 1217, 1054, 1057, 1044, 1044, 124, 124, 125, 125, 125, + /* 1170 */ 125, 1498, 478, 1271, 417, 123, 123, 123, 123, 122, + /* 1180 */ 122, 121, 121, 121, 120, 117, 448, 205, 1213, 495, + /* 1190 */ 430, 867, 468, 322, 495, 123, 123, 123, 123, 122, + /* 1200 */ 122, 121, 121, 121, 120, 117, 448, 572, 557, 1140, + /* 1210 */ 1642, 1422, 1642, 543, 572, 123, 123, 123, 123, 122, + /* 1220 */ 122, 121, 121, 121, 120, 117, 448, 572, 1422, 572, + /* 1230 */ 13, 13, 542, 323, 1325, 411, 334, 58, 58, 349, + /* 1240 */ 1422, 1170, 326, 286, 286, 549, 1213, 300, 895, 530, + /* 1250 */ 45, 45, 59, 59, 1140, 1643, 569, 1643, 565, 417, + /* 1260 */ 127, 81, 1217, 1217, 1054, 1057, 1044, 1044, 124, 124, + /* 1270 */ 125, 125, 125, 125, 1367, 373, 500, 290, 1193, 512, + /* 1280 */ 1366, 427, 394, 394, 393, 275, 391, 896, 1138, 852, + /* 1290 */ 478, 258, 1422, 1170, 463, 1159, 12, 331, 428, 333, + /* 1300 */ 1117, 460, 236, 258, 325, 460, 544, 1544, 1159, 1098, + /* 1310 */ 491, 1159, 324, 1098, 440, 1118, 335, 516, 123, 123, + /* 1320 */ 123, 123, 122, 122, 121, 121, 121, 120, 117, 448, + /* 1330 */ 1119, 318, 563, 1138, 572, 1193, 1194, 1193, 112, 564, + /* 1340 */ 201, 4, 238, 433, 935, 490, 285, 228, 1517, 934, + /* 1350 */ 170, 560, 572, 142, 1516, 567, 572, 60, 60, 572, + /* 1360 */ 416, 572, 441, 572, 535, 302, 875, 8, 487, 572, + /* 1370 */ 237, 572, 416, 572, 485, 61, 61, 572, 449, 62, + /* 1380 */ 62, 332, 63, 63, 46, 46, 47, 47, 361, 572, + /* 1390 */ 561, 572, 48, 48, 50, 50, 51, 51, 572, 295, + /* 1400 */ 64, 64, 482, 295, 539, 412, 471, 1031, 572, 538, + /* 1410 */ 318, 563, 65, 65, 66, 66, 409, 475, 572, 1031, + /* 1420 */ 572, 14, 14, 875, 1020, 110, 110, 409, 1019, 572, + /* 1430 */ 474, 67, 67, 111, 455, 449, 573, 449, 98, 317, + /* 1440 */ 1019, 132, 132, 133, 133, 572, 1561, 572, 974, 409, + /* 1450 */ 6, 1562, 68, 68, 1560, 6, 975, 572, 6, 1559, + /* 1460 */ 1019, 1019, 1021, 6, 346, 218, 101, 531, 53, 53, + /* 1470 */ 69, 69, 1019, 1019, 1021, 1022, 28, 1586, 1181, 451, + /* 1480 */ 70, 70, 290, 87, 215, 31, 1363, 394, 394, 393, + /* 1490 */ 275, 391, 350, 109, 852, 107, 572, 112, 564, 483, + /* 1500 */ 4, 1212, 572, 239, 153, 572, 39, 236, 1299, 325, + /* 1510 */ 112, 564, 1298, 4, 567, 572, 32, 324, 572, 54, + /* 1520 */ 54, 572, 1135, 353, 398, 165, 165, 567, 166, 166, + /* 1530 */ 572, 291, 355, 572, 17, 357, 572, 449, 77, 77, + /* 1540 */ 1313, 55, 55, 1297, 73, 73, 572, 238, 470, 561, + /* 1550 */ 449, 472, 364, 135, 135, 170, 74, 74, 142, 163, + /* 1560 */ 163, 374, 561, 539, 572, 321, 572, 886, 540, 137, + /* 1570 */ 137, 339, 1353, 422, 298, 237, 539, 572, 1031, 572, + /* 1580 */ 340, 538, 101, 369, 110, 110, 162, 131, 131, 164, + /* 1590 */ 164, 1031, 111, 368, 449, 573, 449, 110, 110, 1019, + /* 1600 */ 157, 157, 141, 141, 572, 111, 572, 449, 573, 449, + /* 1610 */ 412, 288, 1019, 572, 882, 318, 563, 572, 219, 572, + /* 1620 */ 241, 1012, 477, 263, 263, 894, 893, 140, 140, 138, + /* 1630 */ 138, 1019, 1019, 1021, 1022, 28, 139, 139, 525, 455, + /* 1640 */ 76, 76, 78, 78, 1019, 1019, 1021, 1022, 28, 1181, + /* 1650 */ 451, 572, 1083, 290, 112, 564, 1575, 4, 394, 394, + /* 1660 */ 393, 275, 391, 572, 1023, 852, 572, 479, 345, 263, + /* 1670 */ 101, 567, 882, 1376, 75, 75, 1421, 501, 236, 260, + /* 1680 */ 325, 112, 564, 359, 4, 101, 43, 43, 324, 49, + /* 1690 */ 49, 901, 902, 161, 449, 101, 977, 978, 567, 1079, + /* 1700 */ 1349, 260, 965, 932, 263, 114, 561, 1095, 517, 1095, + /* 1710 */ 1083, 1094, 865, 1094, 151, 933, 1144, 114, 238, 1361, + /* 1720 */ 558, 449, 1023, 559, 1426, 1278, 170, 1269, 1257, 142, + /* 1730 */ 1601, 1256, 1258, 561, 1594, 1031, 496, 278, 213, 1346, + /* 1740 */ 310, 110, 110, 939, 311, 312, 237, 11, 234, 111, + /* 1750 */ 221, 449, 573, 449, 293, 395, 1019, 1408, 337, 1403, + /* 1760 */ 1396, 338, 1031, 299, 343, 1413, 1412, 481, 110, 110, + /* 1770 */ 506, 402, 225, 1296, 206, 367, 111, 1358, 449, 573, + /* 1780 */ 449, 412, 1359, 1019, 1489, 1488, 318, 563, 1019, 1019, + /* 1790 */ 1021, 1022, 28, 562, 207, 220, 80, 564, 389, 4, + /* 1800 */ 1597, 1357, 552, 1356, 1233, 181, 267, 232, 1536, 1534, + /* 1810 */ 455, 1230, 420, 567, 82, 1019, 1019, 1021, 1022, 28, + /* 1820 */ 86, 217, 85, 1494, 190, 175, 183, 465, 185, 466, + /* 1830 */ 36, 1409, 186, 187, 188, 499, 449, 244, 37, 99, + /* 1840 */ 400, 1415, 1414, 488, 1417, 194, 473, 403, 561, 1483, + /* 1850 */ 248, 92, 1505, 494, 198, 279, 112, 564, 250, 4, + /* 1860 */ 348, 497, 405, 352, 1259, 251, 252, 515, 1316, 434, + /* 1870 */ 1315, 1314, 94, 567, 1307, 886, 1306, 1031, 226, 406, + /* 1880 */ 1611, 1610, 438, 110, 110, 1580, 1286, 524, 439, 308, + /* 1890 */ 266, 111, 1285, 449, 573, 449, 449, 309, 1019, 366, + /* 1900 */ 1284, 1609, 265, 1566, 1565, 442, 372, 1381, 561, 129, + /* 1910 */ 550, 1380, 10, 1470, 383, 106, 316, 551, 100, 35, + /* 1920 */ 534, 575, 212, 1339, 381, 387, 1187, 1338, 274, 276, + /* 1930 */ 1019, 1019, 1021, 1022, 28, 277, 413, 1031, 576, 1254, + /* 1940 */ 388, 1521, 1249, 110, 110, 167, 1522, 168, 148, 1520, + /* 1950 */ 1519, 111, 306, 449, 573, 449, 222, 223, 1019, 839, + /* 1960 */ 169, 79, 450, 214, 414, 233, 320, 145, 1093, 1091, + /* 1970 */ 328, 182, 171, 1212, 918, 184, 240, 336, 243, 1107, + /* 1980 */ 189, 172, 173, 423, 425, 88, 180, 191, 89, 90, + /* 1990 */ 1019, 1019, 1021, 1022, 28, 91, 174, 1110, 245, 1106, + /* 2000 */ 246, 159, 18, 247, 347, 1099, 263, 195, 1227, 493, + /* 2010 */ 249, 196, 38, 854, 498, 368, 253, 360, 897, 197, + /* 2020 */ 502, 93, 19, 20, 507, 884, 363, 510, 95, 307, + /* 2030 */ 160, 96, 518, 97, 1175, 1060, 1146, 40, 21, 227, + /* 2040 */ 176, 1145, 282, 284, 969, 200, 963, 114, 262, 1165, + /* 2050 */ 22, 23, 24, 1161, 1169, 25, 1163, 1150, 34, 26, + /* 2060 */ 1168, 546, 27, 204, 101, 103, 104, 1074, 7, 1061, + /* 2070 */ 1059, 1063, 1116, 1064, 1115, 268, 269, 29, 41, 270, + /* 2080 */ 1024, 866, 113, 30, 568, 392, 1183, 144, 178, 1182, + /* 2090 */ 271, 928, 1245, 1245, 1245, 1245, 1245, 1245, 1245, 1602, }; static const YYCODETYPE yy_lookahead[] = { /* 0 */ 193, 193, 193, 274, 275, 276, 193, 274, 275, 276, @@ -170101,7 +171786,7 @@ static const YYCODETYPE yy_lookahead[] = { /* 730 */ 108, 109, 110, 111, 112, 113, 43, 44, 45, 46, /* 740 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, /* 750 */ 57, 19, 193, 193, 59, 23, 116, 117, 118, 59, - /* 760 */ 201, 21, 241, 304, 22, 206, 127, 128, 129, 193, + /* 760 */ 201, 21, 241, 304, 193, 206, 127, 128, 129, 193, /* 770 */ 128, 129, 235, 236, 304, 43, 44, 45, 46, 47, /* 780 */ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, /* 790 */ 22, 193, 216, 217, 193, 102, 103, 104, 105, 106, @@ -170112,129 +171797,129 @@ static const YYCODETYPE yy_lookahead[] = { /* 840 */ 108, 109, 110, 111, 112, 113, 43, 44, 45, 46, /* 850 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, /* 860 */ 57, 19, 193, 123, 76, 239, 240, 193, 253, 239, - /* 870 */ 240, 239, 240, 193, 106, 107, 193, 89, 252, 193, - /* 880 */ 92, 59, 252, 141, 252, 43, 44, 45, 46, 47, + /* 870 */ 240, 239, 240, 244, 106, 107, 193, 89, 252, 193, + /* 880 */ 92, 59, 252, 254, 252, 43, 44, 45, 46, 47, /* 890 */ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, /* 900 */ 284, 161, 216, 217, 193, 102, 103, 104, 105, 106, - /* 910 */ 107, 108, 109, 110, 111, 112, 113, 231, 193, 16, - /* 920 */ 187, 188, 189, 190, 7, 8, 9, 309, 195, 25, + /* 910 */ 107, 108, 109, 110, 111, 112, 113, 231, 193, 244, + /* 920 */ 187, 188, 189, 190, 7, 8, 9, 309, 195, 254, /* 930 */ 197, 313, 19, 127, 128, 129, 262, 204, 22, 117, - /* 940 */ 24, 216, 217, 263, 102, 103, 104, 105, 106, 107, + /* 940 */ 24, 216, 217, 273, 102, 103, 104, 105, 106, 107, /* 950 */ 108, 109, 110, 111, 112, 113, 43, 44, 45, 46, /* 960 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, /* 970 */ 57, 193, 239, 240, 193, 59, 19, 188, 253, 190, - /* 980 */ 77, 226, 79, 193, 195, 252, 197, 193, 19, 301, - /* 990 */ 302, 193, 193, 204, 216, 217, 226, 216, 217, 266, + /* 980 */ 193, 311, 312, 16, 195, 252, 197, 193, 19, 301, + /* 990 */ 302, 135, 193, 204, 216, 217, 140, 216, 217, 266, /* 1000 */ 204, 159, 45, 46, 47, 48, 49, 50, 51, 52, /* 1010 */ 53, 54, 55, 56, 57, 102, 103, 104, 105, 106, /* 1020 */ 107, 108, 109, 110, 111, 112, 113, 12, 239, 240, - /* 1030 */ 232, 298, 238, 117, 253, 239, 240, 238, 259, 260, - /* 1040 */ 193, 252, 27, 31, 193, 193, 142, 204, 252, 193, - /* 1050 */ 193, 39, 262, 193, 100, 266, 278, 42, 204, 102, + /* 1030 */ 193, 298, 238, 117, 253, 239, 240, 238, 259, 260, + /* 1040 */ 193, 252, 27, 193, 77, 193, 79, 204, 252, 262, + /* 1050 */ 193, 299, 300, 193, 100, 266, 278, 42, 204, 102, /* 1060 */ 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, /* 1070 */ 113, 117, 159, 216, 217, 121, 216, 217, 63, 193, - /* 1080 */ 193, 193, 239, 240, 115, 116, 193, 298, 73, 238, + /* 1080 */ 193, 193, 239, 240, 115, 116, 193, 298, 73, 240, /* 1090 */ 238, 231, 19, 239, 240, 252, 22, 24, 211, 212, - /* 1100 */ 24, 193, 216, 217, 216, 217, 252, 153, 154, 155, - /* 1110 */ 253, 16, 19, 144, 213, 268, 43, 44, 45, 46, + /* 1100 */ 263, 252, 216, 217, 216, 217, 252, 153, 154, 155, + /* 1110 */ 253, 193, 19, 144, 213, 268, 43, 44, 45, 46, /* 1120 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, - /* 1130 */ 57, 238, 19, 59, 193, 59, 43, 44, 45, 46, + /* 1130 */ 57, 193, 19, 59, 216, 217, 43, 44, 45, 46, /* 1140 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, - /* 1150 */ 57, 22, 23, 193, 25, 193, 43, 44, 45, 46, + /* 1150 */ 57, 193, 19, 24, 216, 217, 43, 44, 45, 46, /* 1160 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, - /* 1170 */ 57, 284, 77, 193, 79, 102, 103, 104, 105, 106, - /* 1180 */ 107, 108, 109, 110, 111, 112, 113, 286, 193, 193, - /* 1190 */ 193, 117, 291, 117, 232, 102, 103, 104, 105, 106, - /* 1200 */ 107, 108, 109, 110, 111, 112, 113, 204, 22, 23, - /* 1210 */ 66, 25, 216, 217, 35, 102, 103, 104, 105, 106, - /* 1220 */ 107, 108, 109, 110, 111, 112, 113, 193, 268, 85, - /* 1230 */ 101, 193, 309, 309, 240, 19, 313, 313, 94, 208, - /* 1240 */ 209, 193, 239, 240, 193, 66, 252, 19, 268, 244, - /* 1250 */ 216, 217, 193, 74, 213, 252, 161, 19, 263, 254, + /* 1170 */ 57, 284, 193, 208, 209, 102, 103, 104, 105, 106, + /* 1180 */ 107, 108, 109, 110, 111, 112, 113, 286, 59, 193, + /* 1190 */ 232, 117, 291, 193, 193, 102, 103, 104, 105, 106, + /* 1200 */ 107, 108, 109, 110, 111, 112, 113, 193, 204, 22, + /* 1210 */ 23, 193, 25, 66, 193, 102, 103, 104, 105, 106, + /* 1220 */ 107, 108, 109, 110, 111, 112, 113, 193, 193, 193, + /* 1230 */ 216, 217, 85, 193, 238, 19, 16, 216, 217, 238, + /* 1240 */ 193, 94, 193, 239, 240, 231, 117, 268, 35, 116, + /* 1250 */ 216, 217, 216, 217, 22, 23, 252, 25, 208, 209, /* 1260 */ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, - /* 1270 */ 54, 55, 56, 57, 193, 216, 217, 5, 59, 193, - /* 1280 */ 19, 244, 10, 11, 12, 13, 14, 101, 309, 17, - /* 1290 */ 146, 254, 313, 193, 193, 76, 115, 216, 217, 309, - /* 1300 */ 12, 263, 30, 313, 32, 46, 87, 46, 89, 130, - /* 1310 */ 193, 92, 40, 22, 263, 27, 216, 217, 102, 103, + /* 1270 */ 54, 55, 56, 57, 193, 193, 19, 5, 59, 66, + /* 1280 */ 193, 263, 10, 11, 12, 13, 14, 74, 101, 17, + /* 1290 */ 193, 46, 193, 146, 193, 76, 213, 77, 263, 79, + /* 1300 */ 12, 260, 30, 46, 32, 264, 87, 193, 89, 29, + /* 1310 */ 263, 92, 40, 33, 232, 27, 193, 108, 102, 103, /* 1320 */ 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, - /* 1330 */ 42, 150, 291, 216, 217, 116, 117, 118, 19, 20, - /* 1340 */ 193, 22, 70, 260, 116, 193, 24, 264, 193, 263, - /* 1350 */ 78, 63, 61, 81, 116, 36, 193, 260, 193, 29, - /* 1360 */ 193, 264, 193, 33, 145, 193, 59, 48, 216, 217, - /* 1370 */ 98, 216, 217, 193, 115, 193, 115, 193, 59, 216, - /* 1380 */ 217, 216, 217, 216, 217, 216, 217, 255, 216, 217, - /* 1390 */ 71, 193, 131, 193, 25, 65, 216, 217, 216, 217, - /* 1400 */ 216, 217, 208, 209, 85, 133, 193, 100, 193, 90, - /* 1410 */ 138, 139, 138, 139, 216, 217, 216, 217, 193, 100, - /* 1420 */ 193, 108, 135, 116, 117, 106, 107, 140, 121, 216, - /* 1430 */ 217, 216, 217, 114, 162, 116, 117, 118, 299, 300, - /* 1440 */ 121, 216, 217, 216, 217, 193, 244, 193, 135, 244, - /* 1450 */ 193, 256, 257, 140, 244, 193, 254, 193, 193, 254, - /* 1460 */ 153, 154, 155, 141, 254, 149, 150, 258, 216, 217, + /* 1330 */ 42, 138, 139, 101, 193, 116, 117, 118, 19, 20, + /* 1340 */ 255, 22, 70, 130, 135, 65, 256, 257, 193, 140, + /* 1350 */ 78, 63, 193, 81, 193, 36, 193, 216, 217, 193, + /* 1360 */ 115, 193, 263, 193, 145, 268, 59, 48, 193, 193, + /* 1370 */ 98, 193, 115, 193, 291, 216, 217, 193, 59, 216, + /* 1380 */ 217, 161, 216, 217, 216, 217, 216, 217, 131, 193, + /* 1390 */ 71, 193, 216, 217, 216, 217, 216, 217, 193, 260, + /* 1400 */ 216, 217, 19, 264, 85, 133, 244, 100, 193, 90, + /* 1410 */ 138, 139, 216, 217, 216, 217, 254, 244, 193, 100, + /* 1420 */ 193, 216, 217, 116, 117, 106, 107, 254, 121, 193, + /* 1430 */ 115, 216, 217, 114, 162, 116, 117, 118, 115, 244, + /* 1440 */ 121, 216, 217, 216, 217, 193, 309, 193, 31, 254, + /* 1450 */ 313, 309, 216, 217, 309, 313, 39, 193, 313, 309, + /* 1460 */ 153, 154, 155, 313, 193, 150, 25, 144, 216, 217, /* 1470 */ 216, 217, 153, 154, 155, 156, 157, 0, 1, 2, - /* 1480 */ 216, 217, 5, 115, 158, 193, 160, 10, 11, 12, - /* 1490 */ 13, 14, 193, 59, 17, 126, 193, 19, 20, 129, - /* 1500 */ 22, 193, 22, 22, 24, 193, 23, 30, 25, 32, - /* 1510 */ 19, 20, 144, 22, 36, 216, 217, 40, 193, 216, - /* 1520 */ 217, 193, 152, 129, 216, 217, 193, 36, 216, 217, - /* 1530 */ 193, 99, 193, 193, 53, 193, 193, 59, 23, 193, - /* 1540 */ 25, 216, 217, 193, 216, 217, 152, 70, 59, 71, - /* 1550 */ 59, 117, 193, 216, 217, 78, 216, 217, 81, 216, - /* 1560 */ 217, 318, 71, 85, 193, 133, 193, 193, 90, 23, - /* 1570 */ 23, 25, 25, 120, 121, 98, 85, 193, 100, 193, - /* 1580 */ 23, 90, 25, 121, 106, 107, 19, 216, 217, 216, + /* 1480 */ 216, 217, 5, 149, 150, 22, 193, 10, 11, 12, + /* 1490 */ 13, 14, 193, 158, 17, 160, 193, 19, 20, 116, + /* 1500 */ 22, 25, 193, 24, 22, 193, 24, 30, 226, 32, + /* 1510 */ 19, 20, 226, 22, 36, 193, 53, 40, 193, 216, + /* 1520 */ 217, 193, 23, 193, 25, 216, 217, 36, 216, 217, + /* 1530 */ 193, 99, 193, 193, 22, 193, 193, 59, 216, 217, + /* 1540 */ 193, 216, 217, 193, 216, 217, 193, 70, 129, 71, + /* 1550 */ 59, 129, 193, 216, 217, 78, 216, 217, 81, 216, + /* 1560 */ 217, 193, 71, 85, 193, 133, 193, 126, 90, 216, + /* 1570 */ 217, 152, 258, 61, 152, 98, 85, 193, 100, 193, + /* 1580 */ 23, 90, 25, 121, 106, 107, 23, 216, 217, 216, /* 1590 */ 217, 100, 114, 131, 116, 117, 118, 106, 107, 121, - /* 1600 */ 216, 217, 216, 217, 193, 114, 117, 116, 117, 118, - /* 1610 */ 133, 193, 121, 193, 193, 138, 139, 193, 23, 193, - /* 1620 */ 25, 23, 23, 25, 25, 7, 8, 216, 217, 193, - /* 1630 */ 193, 153, 154, 155, 156, 157, 216, 217, 193, 162, + /* 1600 */ 216, 217, 216, 217, 193, 114, 193, 116, 117, 118, + /* 1610 */ 133, 22, 121, 193, 59, 138, 139, 193, 142, 193, + /* 1620 */ 141, 23, 23, 25, 25, 120, 121, 216, 217, 216, + /* 1630 */ 217, 153, 154, 155, 156, 157, 216, 217, 19, 162, /* 1640 */ 216, 217, 216, 217, 153, 154, 155, 156, 157, 1, - /* 1650 */ 2, 193, 193, 5, 19, 20, 59, 22, 10, 11, - /* 1660 */ 12, 13, 14, 193, 97, 17, 193, 23, 193, 25, - /* 1670 */ 288, 36, 193, 242, 216, 217, 236, 23, 30, 25, + /* 1650 */ 2, 193, 59, 5, 19, 20, 318, 22, 10, 11, + /* 1660 */ 12, 13, 14, 193, 59, 17, 193, 23, 23, 25, + /* 1670 */ 25, 36, 117, 193, 216, 217, 193, 23, 30, 25, /* 1680 */ 32, 19, 20, 23, 22, 25, 216, 217, 40, 216, - /* 1690 */ 217, 216, 217, 193, 59, 216, 217, 193, 36, 83, - /* 1700 */ 84, 153, 153, 155, 155, 23, 71, 25, 23, 193, - /* 1710 */ 25, 193, 193, 193, 117, 193, 193, 193, 70, 193, - /* 1720 */ 193, 59, 193, 255, 255, 287, 78, 255, 243, 81, - /* 1730 */ 191, 255, 297, 71, 271, 100, 293, 245, 267, 214, - /* 1740 */ 246, 106, 107, 108, 246, 271, 98, 245, 293, 114, - /* 1750 */ 220, 116, 117, 118, 267, 271, 121, 271, 225, 219, - /* 1760 */ 229, 219, 100, 219, 259, 259, 259, 259, 106, 107, - /* 1770 */ 249, 196, 60, 280, 141, 243, 114, 249, 116, 117, - /* 1780 */ 118, 133, 245, 121, 200, 297, 138, 139, 153, 154, - /* 1790 */ 155, 156, 157, 297, 200, 38, 19, 20, 151, 22, - /* 1800 */ 200, 150, 140, 294, 294, 22, 272, 43, 234, 18, - /* 1810 */ 162, 270, 200, 36, 237, 153, 154, 155, 156, 157, - /* 1820 */ 237, 283, 237, 237, 18, 199, 149, 246, 272, 270, - /* 1830 */ 272, 200, 158, 246, 246, 234, 59, 234, 246, 199, - /* 1840 */ 290, 62, 289, 200, 199, 22, 221, 115, 71, 200, - /* 1850 */ 200, 199, 199, 221, 218, 218, 19, 20, 64, 22, - /* 1860 */ 218, 227, 22, 224, 126, 224, 165, 221, 24, 305, - /* 1870 */ 200, 113, 312, 36, 218, 220, 218, 100, 282, 218, - /* 1880 */ 91, 218, 317, 106, 107, 221, 227, 282, 317, 82, - /* 1890 */ 148, 114, 265, 116, 117, 118, 59, 145, 121, 22, - /* 1900 */ 277, 158, 200, 265, 25, 202, 147, 250, 71, 279, - /* 1910 */ 13, 146, 194, 194, 249, 248, 250, 140, 247, 246, - /* 1920 */ 6, 192, 192, 192, 303, 303, 213, 207, 300, 213, - /* 1930 */ 153, 154, 155, 156, 157, 213, 213, 100, 213, 222, - /* 1940 */ 207, 214, 214, 106, 107, 4, 222, 207, 3, 22, - /* 1950 */ 163, 114, 15, 116, 117, 118, 16, 23, 121, 23, - /* 1960 */ 139, 151, 130, 25, 142, 16, 24, 20, 144, 1, - /* 1970 */ 142, 130, 130, 61, 53, 53, 37, 151, 53, 53, - /* 1980 */ 130, 116, 34, 1, 141, 5, 22, 115, 161, 141, - /* 1990 */ 153, 154, 155, 156, 157, 25, 68, 68, 75, 41, - /* 2000 */ 115, 24, 131, 20, 19, 125, 22, 96, 22, 22, - /* 2010 */ 67, 23, 22, 67, 59, 24, 22, 28, 67, 23, - /* 2020 */ 22, 22, 149, 23, 23, 23, 116, 23, 25, 37, - /* 2030 */ 97, 141, 23, 23, 22, 143, 25, 75, 88, 34, - /* 2040 */ 34, 34, 34, 86, 75, 93, 23, 34, 22, 34, - /* 2050 */ 25, 24, 34, 25, 23, 142, 23, 142, 44, 23, - /* 2060 */ 23, 23, 11, 23, 25, 22, 22, 22, 15, 23, - /* 2070 */ 23, 22, 22, 25, 1, 1, 141, 25, 23, 135, - /* 2080 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, - /* 2090 */ 319, 319, 319, 319, 141, 141, 319, 319, 319, 319, + /* 1690 */ 217, 7, 8, 23, 59, 25, 83, 84, 36, 23, + /* 1700 */ 193, 25, 23, 23, 25, 25, 71, 153, 145, 155, + /* 1710 */ 117, 153, 23, 155, 25, 23, 97, 25, 70, 193, + /* 1720 */ 193, 59, 117, 236, 193, 193, 78, 193, 193, 81, + /* 1730 */ 141, 193, 193, 71, 193, 100, 288, 287, 242, 255, + /* 1740 */ 255, 106, 107, 108, 255, 255, 98, 243, 297, 114, + /* 1750 */ 214, 116, 117, 118, 245, 191, 121, 271, 293, 267, + /* 1760 */ 267, 246, 100, 246, 245, 271, 271, 293, 106, 107, + /* 1770 */ 220, 271, 229, 225, 249, 219, 114, 259, 116, 117, + /* 1780 */ 118, 133, 259, 121, 219, 219, 138, 139, 153, 154, + /* 1790 */ 155, 156, 157, 280, 249, 243, 19, 20, 245, 22, + /* 1800 */ 196, 259, 140, 259, 60, 297, 141, 297, 200, 200, + /* 1810 */ 162, 38, 200, 36, 294, 153, 154, 155, 156, 157, + /* 1820 */ 151, 150, 294, 283, 22, 43, 234, 18, 237, 200, + /* 1830 */ 270, 272, 237, 237, 237, 18, 59, 199, 270, 149, + /* 1840 */ 246, 272, 272, 200, 234, 234, 246, 246, 71, 246, + /* 1850 */ 199, 158, 290, 62, 22, 200, 19, 20, 199, 22, + /* 1860 */ 289, 221, 221, 200, 200, 199, 199, 115, 218, 64, + /* 1870 */ 218, 218, 22, 36, 227, 126, 227, 100, 165, 221, + /* 1880 */ 224, 224, 24, 106, 107, 312, 218, 305, 113, 282, + /* 1890 */ 91, 114, 220, 116, 117, 118, 59, 282, 121, 218, + /* 1900 */ 218, 218, 200, 317, 317, 82, 221, 265, 71, 148, + /* 1910 */ 145, 265, 22, 277, 200, 158, 279, 140, 147, 25, + /* 1920 */ 146, 202, 248, 250, 249, 247, 13, 250, 194, 194, + /* 1930 */ 153, 154, 155, 156, 157, 6, 303, 100, 192, 192, + /* 1940 */ 246, 213, 192, 106, 107, 207, 213, 207, 222, 213, + /* 1950 */ 213, 114, 222, 116, 117, 118, 214, 214, 121, 4, + /* 1960 */ 207, 213, 3, 22, 303, 15, 163, 16, 23, 23, + /* 1970 */ 139, 151, 130, 25, 20, 142, 24, 16, 144, 1, + /* 1980 */ 142, 130, 130, 61, 37, 53, 300, 151, 53, 53, + /* 1990 */ 153, 154, 155, 156, 157, 53, 130, 116, 34, 1, + /* 2000 */ 141, 5, 22, 115, 161, 68, 25, 68, 75, 41, + /* 2010 */ 141, 115, 24, 20, 19, 131, 125, 23, 28, 22, + /* 2020 */ 67, 22, 22, 22, 67, 59, 24, 96, 22, 67, + /* 2030 */ 23, 149, 22, 25, 23, 23, 23, 22, 34, 141, + /* 2040 */ 37, 97, 23, 23, 116, 22, 143, 25, 34, 75, + /* 2050 */ 34, 34, 34, 88, 75, 34, 86, 23, 22, 34, + /* 2060 */ 93, 24, 34, 25, 25, 142, 142, 23, 44, 23, + /* 2070 */ 23, 23, 23, 11, 23, 25, 22, 22, 22, 141, + /* 2080 */ 23, 23, 22, 22, 25, 15, 1, 23, 25, 1, + /* 2090 */ 141, 135, 319, 319, 319, 319, 319, 319, 319, 141, /* 2100 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, /* 2110 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, /* 2120 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, @@ -170253,176 +171938,177 @@ static const YYCODETYPE yy_lookahead[] = { /* 2250 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, /* 2260 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, /* 2270 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, - /* 2280 */ 319, + /* 2280 */ 319, 319, 319, 319, 319, }; -#define YY_SHIFT_COUNT (574) +#define YY_SHIFT_COUNT (578) #define YY_SHIFT_MIN (0) -#define YY_SHIFT_MAX (2074) +#define YY_SHIFT_MAX (2088) static const unsigned short int yy_shift_ofst[] = { /* 0 */ 1648, 1477, 1272, 322, 322, 1, 1319, 1478, 1491, 1837, /* 10 */ 1837, 1837, 471, 0, 0, 214, 1093, 1837, 1837, 1837, /* 20 */ 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, - /* 30 */ 271, 271, 1219, 1219, 216, 88, 1, 1, 1, 1, - /* 40 */ 1, 40, 111, 258, 361, 469, 512, 583, 622, 693, - /* 50 */ 732, 803, 842, 913, 1073, 1093, 1093, 1093, 1093, 1093, + /* 30 */ 1837, 271, 271, 1219, 1219, 216, 88, 1, 1, 1, + /* 40 */ 1, 1, 40, 111, 258, 361, 469, 512, 583, 622, + /* 50 */ 693, 732, 803, 842, 913, 1073, 1093, 1093, 1093, 1093, /* 60 */ 1093, 1093, 1093, 1093, 1093, 1093, 1093, 1093, 1093, 1093, - /* 70 */ 1093, 1093, 1093, 1113, 1093, 1216, 957, 957, 1635, 1662, - /* 80 */ 1777, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, + /* 70 */ 1093, 1093, 1093, 1093, 1113, 1093, 1216, 957, 957, 1635, + /* 80 */ 1662, 1777, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, /* 90 */ 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, /* 100 */ 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, /* 110 */ 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, /* 120 */ 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, - /* 130 */ 137, 181, 181, 181, 181, 181, 181, 181, 94, 430, - /* 140 */ 66, 65, 112, 366, 533, 533, 740, 1261, 533, 533, - /* 150 */ 79, 79, 533, 412, 412, 412, 77, 412, 123, 113, - /* 160 */ 113, 22, 22, 2096, 2096, 328, 328, 328, 239, 468, - /* 170 */ 468, 468, 468, 1015, 1015, 409, 366, 1129, 1186, 533, - /* 180 */ 533, 533, 533, 533, 533, 533, 533, 533, 533, 533, - /* 190 */ 533, 533, 533, 533, 533, 533, 533, 533, 533, 969, - /* 200 */ 621, 621, 533, 642, 788, 788, 1228, 1228, 822, 822, - /* 210 */ 67, 1274, 2096, 2096, 2096, 2096, 2096, 2096, 2096, 1307, - /* 220 */ 954, 954, 585, 472, 640, 387, 695, 538, 541, 700, - /* 230 */ 533, 533, 533, 533, 533, 533, 533, 533, 533, 533, - /* 240 */ 222, 533, 533, 533, 533, 533, 533, 533, 533, 533, - /* 250 */ 533, 533, 533, 1179, 1179, 1179, 533, 533, 533, 565, - /* 260 */ 533, 533, 533, 916, 1144, 533, 533, 1288, 533, 533, - /* 270 */ 533, 533, 533, 533, 533, 533, 639, 1330, 209, 1076, - /* 280 */ 1076, 1076, 1076, 580, 209, 209, 1313, 768, 917, 649, - /* 290 */ 1181, 1316, 405, 1316, 1238, 249, 1181, 1181, 249, 1181, - /* 300 */ 405, 1238, 1369, 464, 1259, 1012, 1012, 1012, 1368, 1368, - /* 310 */ 1368, 1368, 184, 184, 1326, 904, 1287, 1480, 1712, 1712, - /* 320 */ 1633, 1633, 1757, 1757, 1633, 1647, 1651, 1783, 1764, 1791, - /* 330 */ 1791, 1791, 1791, 1633, 1806, 1677, 1651, 1651, 1677, 1783, - /* 340 */ 1764, 1677, 1764, 1677, 1633, 1806, 1674, 1779, 1633, 1806, - /* 350 */ 1823, 1633, 1806, 1633, 1806, 1823, 1732, 1732, 1732, 1794, - /* 360 */ 1840, 1840, 1823, 1732, 1738, 1732, 1794, 1732, 1732, 1701, - /* 370 */ 1844, 1758, 1758, 1823, 1633, 1789, 1789, 1807, 1807, 1742, - /* 380 */ 1752, 1877, 1633, 1743, 1742, 1759, 1765, 1677, 1879, 1897, - /* 390 */ 1897, 1914, 1914, 1914, 2096, 2096, 2096, 2096, 2096, 2096, - /* 400 */ 2096, 2096, 2096, 2096, 2096, 2096, 2096, 2096, 2096, 207, - /* 410 */ 1095, 331, 620, 903, 806, 1074, 1483, 1432, 1481, 1322, - /* 420 */ 1370, 1394, 1515, 1291, 1546, 1547, 1557, 1595, 1598, 1599, - /* 430 */ 1434, 1453, 1618, 1462, 1567, 1489, 1644, 1654, 1616, 1660, - /* 440 */ 1548, 1549, 1682, 1685, 1597, 742, 1941, 1945, 1927, 1787, - /* 450 */ 1937, 1940, 1934, 1936, 1821, 1810, 1832, 1938, 1938, 1942, - /* 460 */ 1822, 1947, 1824, 1949, 1968, 1828, 1841, 1938, 1842, 1912, - /* 470 */ 1939, 1938, 1826, 1921, 1922, 1925, 1926, 1850, 1865, 1948, - /* 480 */ 1843, 1982, 1980, 1964, 1872, 1827, 1928, 1970, 1929, 1923, - /* 490 */ 1958, 1848, 1885, 1977, 1983, 1985, 1871, 1880, 1984, 1943, - /* 500 */ 1986, 1987, 1988, 1990, 1946, 1955, 1991, 1911, 1989, 1994, - /* 510 */ 1951, 1992, 1996, 1873, 1998, 2000, 2001, 2002, 2003, 2004, - /* 520 */ 1999, 1933, 1890, 2009, 2010, 1910, 2005, 2012, 1892, 2011, - /* 530 */ 2006, 2007, 2008, 2013, 1950, 1962, 1957, 2014, 1969, 1952, - /* 540 */ 2015, 2023, 2026, 2027, 2025, 2028, 2018, 1913, 1915, 2031, - /* 550 */ 2011, 2033, 2036, 2037, 2038, 2039, 2040, 2043, 2051, 2044, - /* 560 */ 2045, 2046, 2047, 2049, 2050, 2048, 1944, 1935, 1953, 1954, - /* 570 */ 2052, 2055, 2053, 2073, 2074, + /* 130 */ 1837, 137, 181, 181, 181, 181, 181, 181, 181, 94, + /* 140 */ 430, 66, 65, 112, 366, 533, 533, 740, 1257, 533, + /* 150 */ 533, 79, 79, 533, 412, 412, 412, 77, 412, 123, + /* 160 */ 113, 113, 113, 22, 22, 2100, 2100, 328, 328, 328, + /* 170 */ 239, 468, 468, 468, 468, 1015, 1015, 409, 366, 1187, + /* 180 */ 1232, 533, 533, 533, 533, 533, 533, 533, 533, 533, + /* 190 */ 533, 533, 533, 533, 533, 533, 533, 533, 533, 533, + /* 200 */ 533, 969, 621, 621, 533, 642, 788, 788, 1133, 1133, + /* 210 */ 822, 822, 67, 1193, 2100, 2100, 2100, 2100, 2100, 2100, + /* 220 */ 2100, 1307, 954, 954, 585, 472, 640, 387, 695, 538, + /* 230 */ 541, 700, 533, 533, 533, 533, 533, 533, 533, 533, + /* 240 */ 533, 533, 222, 533, 533, 533, 533, 533, 533, 533, + /* 250 */ 533, 533, 533, 533, 533, 1213, 1213, 1213, 533, 533, + /* 260 */ 533, 565, 533, 533, 533, 916, 1147, 533, 533, 1288, + /* 270 */ 533, 533, 533, 533, 533, 533, 533, 533, 639, 1280, + /* 280 */ 209, 1129, 1129, 1129, 1129, 580, 209, 209, 1209, 768, + /* 290 */ 917, 649, 1315, 1334, 405, 1334, 1383, 249, 1315, 1315, + /* 300 */ 249, 1315, 405, 1383, 1441, 464, 1245, 1417, 1417, 1417, + /* 310 */ 1323, 1323, 1323, 1323, 184, 184, 1335, 1476, 856, 1482, + /* 320 */ 1744, 1744, 1665, 1665, 1773, 1773, 1665, 1669, 1671, 1802, + /* 330 */ 1782, 1809, 1809, 1809, 1809, 1665, 1817, 1690, 1671, 1671, + /* 340 */ 1690, 1802, 1782, 1690, 1782, 1690, 1665, 1817, 1693, 1791, + /* 350 */ 1665, 1817, 1832, 1665, 1817, 1665, 1817, 1832, 1752, 1752, + /* 360 */ 1752, 1805, 1850, 1850, 1832, 1752, 1749, 1752, 1805, 1752, + /* 370 */ 1752, 1713, 1858, 1775, 1775, 1832, 1665, 1799, 1799, 1823, + /* 380 */ 1823, 1761, 1765, 1890, 1665, 1757, 1761, 1771, 1774, 1690, + /* 390 */ 1894, 1913, 1913, 1929, 1929, 1929, 2100, 2100, 2100, 2100, + /* 400 */ 2100, 2100, 2100, 2100, 2100, 2100, 2100, 2100, 2100, 2100, + /* 410 */ 2100, 207, 1220, 331, 620, 967, 806, 1074, 1499, 1432, + /* 420 */ 1463, 1479, 1419, 1422, 1557, 1512, 1598, 1599, 1644, 1645, + /* 430 */ 1654, 1660, 1555, 1505, 1684, 1462, 1670, 1563, 1619, 1593, + /* 440 */ 1676, 1679, 1613, 1680, 1554, 1558, 1689, 1692, 1605, 1589, + /* 450 */ 1955, 1959, 1941, 1803, 1950, 1951, 1945, 1946, 1831, 1820, + /* 460 */ 1842, 1948, 1948, 1952, 1833, 1954, 1834, 1961, 1978, 1838, + /* 470 */ 1851, 1948, 1852, 1922, 1947, 1948, 1836, 1932, 1935, 1936, + /* 480 */ 1942, 1866, 1881, 1964, 1859, 1998, 1996, 1980, 1888, 1843, + /* 490 */ 1937, 1981, 1939, 1933, 1968, 1869, 1896, 1988, 1993, 1995, + /* 500 */ 1884, 1891, 1997, 1953, 1999, 2000, 1994, 2001, 1957, 1966, + /* 510 */ 2002, 1931, 1990, 2006, 1962, 2003, 2007, 2004, 1882, 2010, + /* 520 */ 2011, 2012, 2008, 2013, 2015, 1944, 1898, 2019, 2020, 1928, + /* 530 */ 2014, 2023, 1903, 2022, 2016, 2017, 2018, 2021, 1965, 1974, + /* 540 */ 1970, 2024, 1979, 1967, 2025, 2034, 2036, 2037, 2038, 2039, + /* 550 */ 2028, 1923, 1924, 2044, 2022, 2046, 2047, 2048, 2049, 2050, + /* 560 */ 2051, 2054, 2062, 2055, 2056, 2057, 2058, 2060, 2061, 2059, + /* 570 */ 1956, 1938, 1949, 1958, 2063, 2064, 2070, 2085, 2088, }; -#define YY_REDUCE_COUNT (408) +#define YY_REDUCE_COUNT (410) #define YY_REDUCE_MIN (-271) -#define YY_REDUCE_MAX (1740) +#define YY_REDUCE_MAX (1753) static const short yy_reduce_ofst[] = { /* 0 */ -125, 733, 789, 241, 293, -123, -193, -191, -183, -187, /* 10 */ 166, 238, 133, -207, -199, -267, -176, -6, 204, 489, - /* 20 */ 576, -175, 598, 686, 615, 725, 860, 778, 781, 857, - /* 30 */ 616, 887, 87, 240, -192, 408, 626, 796, 843, 854, - /* 40 */ 1003, -271, -271, -271, -271, -271, -271, -271, -271, -271, + /* 20 */ 576, 598, -175, 686, 860, 615, 725, 1014, 778, 781, + /* 30 */ 857, 616, 887, 87, 240, -192, 408, 626, 796, 843, + /* 40 */ 854, 1004, -271, -271, -271, -271, -271, -271, -271, -271, /* 50 */ -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, /* 60 */ -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, - /* 70 */ -271, -271, -271, -271, -271, -271, -271, -271, 80, 83, - /* 80 */ 313, 886, 888, 996, 1034, 1059, 1081, 1100, 1117, 1152, - /* 90 */ 1155, 1163, 1165, 1167, 1169, 1172, 1180, 1182, 1184, 1198, - /* 100 */ 1200, 1213, 1215, 1225, 1227, 1252, 1254, 1264, 1299, 1303, - /* 110 */ 1308, 1312, 1325, 1328, 1337, 1340, 1343, 1371, 1373, 1384, - /* 120 */ 1386, 1411, 1420, 1424, 1426, 1458, 1470, 1473, 1475, 1479, - /* 130 */ -271, -271, -271, -271, -271, -271, -271, -271, -271, -271, - /* 140 */ -271, 138, 459, 396, -158, 470, 302, -212, 521, 201, - /* 150 */ -195, -92, 559, 630, 632, 630, -271, 632, 901, 63, - /* 160 */ 407, -271, -271, -271, -271, 161, 161, 161, 251, 335, - /* 170 */ 847, 960, 980, 537, 588, 618, 628, 688, 688, -166, - /* 180 */ -161, 674, 790, 794, 799, 851, 852, -122, 680, -120, - /* 190 */ 995, 1038, 415, 1051, 893, 798, 962, 400, 1086, 779, - /* 200 */ 923, 924, 263, 1041, 979, 990, 1083, 1097, 1031, 1194, - /* 210 */ 362, 994, 1139, 1005, 1037, 1202, 1205, 1195, 1210, -194, - /* 220 */ 56, 185, -135, 232, 522, 560, 601, 617, 669, 683, - /* 230 */ 711, 856, 908, 941, 1048, 1101, 1147, 1257, 1262, 1265, - /* 240 */ 392, 1292, 1333, 1339, 1342, 1346, 1350, 1359, 1374, 1418, - /* 250 */ 1421, 1436, 1437, 593, 755, 770, 997, 1445, 1459, 1209, - /* 260 */ 1500, 1504, 1516, 1132, 1243, 1518, 1519, 1440, 1520, 560, - /* 270 */ 1522, 1523, 1524, 1526, 1527, 1529, 1382, 1438, 1431, 1468, - /* 280 */ 1469, 1472, 1476, 1209, 1431, 1431, 1485, 1525, 1539, 1435, - /* 290 */ 1463, 1471, 1492, 1487, 1443, 1494, 1474, 1484, 1498, 1486, - /* 300 */ 1502, 1455, 1530, 1531, 1533, 1540, 1542, 1544, 1505, 1506, - /* 310 */ 1507, 1508, 1521, 1528, 1493, 1537, 1532, 1575, 1488, 1496, - /* 320 */ 1584, 1594, 1509, 1510, 1600, 1538, 1534, 1541, 1574, 1577, - /* 330 */ 1583, 1585, 1586, 1612, 1626, 1581, 1556, 1558, 1587, 1559, - /* 340 */ 1601, 1588, 1603, 1592, 1631, 1640, 1550, 1553, 1643, 1645, - /* 350 */ 1625, 1649, 1652, 1650, 1653, 1632, 1636, 1637, 1642, 1634, - /* 360 */ 1639, 1641, 1646, 1656, 1655, 1658, 1659, 1661, 1663, 1560, - /* 370 */ 1564, 1596, 1605, 1664, 1670, 1565, 1571, 1627, 1638, 1657, - /* 380 */ 1665, 1623, 1702, 1630, 1666, 1667, 1671, 1673, 1703, 1718, - /* 390 */ 1719, 1729, 1730, 1731, 1621, 1622, 1628, 1720, 1713, 1716, - /* 400 */ 1722, 1723, 1733, 1717, 1724, 1727, 1728, 1725, 1740, + /* 70 */ -271, -271, -271, -271, -271, -271, -271, -271, -271, 80, + /* 80 */ 83, 313, 886, 888, 918, 938, 1021, 1034, 1036, 1141, + /* 90 */ 1159, 1163, 1166, 1168, 1170, 1176, 1178, 1180, 1184, 1196, + /* 100 */ 1198, 1205, 1215, 1225, 1227, 1236, 1252, 1254, 1264, 1303, + /* 110 */ 1309, 1312, 1322, 1325, 1328, 1337, 1340, 1343, 1353, 1371, + /* 120 */ 1373, 1384, 1386, 1411, 1413, 1420, 1424, 1426, 1458, 1470, + /* 130 */ 1473, -271, -271, -271, -271, -271, -271, -271, -271, -271, + /* 140 */ -271, -271, 138, 459, 396, -158, 470, 302, -212, 521, + /* 150 */ 201, -195, -92, 559, 630, 632, 630, -271, 632, 901, + /* 160 */ 63, 407, 670, -271, -271, -271, -271, 161, 161, 161, + /* 170 */ 251, 335, 847, 979, 1097, 537, 588, 618, 628, 688, + /* 180 */ 688, -166, -161, 674, 787, 794, 799, 852, 996, -122, + /* 190 */ 837, -120, 1018, 1035, 415, 1047, 1001, 958, 1082, 400, + /* 200 */ 1099, 779, 1137, 1142, 263, 1083, 1145, 1150, 1041, 1139, + /* 210 */ 965, 1050, 362, 849, 752, 629, 675, 1162, 1173, 1090, + /* 220 */ 1195, -194, 56, 185, -135, 232, 522, 560, 571, 601, + /* 230 */ 617, 669, 683, 711, 850, 893, 1000, 1040, 1049, 1081, + /* 240 */ 1087, 1101, 392, 1114, 1123, 1155, 1161, 1175, 1271, 1293, + /* 250 */ 1299, 1330, 1339, 1342, 1347, 593, 1282, 1286, 1350, 1359, + /* 260 */ 1368, 1314, 1480, 1483, 1507, 1085, 1338, 1526, 1527, 1487, + /* 270 */ 1531, 560, 1532, 1534, 1535, 1538, 1539, 1541, 1448, 1450, + /* 280 */ 1496, 1484, 1485, 1489, 1490, 1314, 1496, 1496, 1504, 1536, + /* 290 */ 1564, 1451, 1486, 1492, 1509, 1493, 1465, 1515, 1494, 1495, + /* 300 */ 1517, 1500, 1519, 1474, 1550, 1543, 1548, 1556, 1565, 1566, + /* 310 */ 1518, 1523, 1542, 1544, 1525, 1545, 1513, 1553, 1552, 1604, + /* 320 */ 1508, 1510, 1608, 1609, 1520, 1528, 1612, 1540, 1559, 1560, + /* 330 */ 1592, 1591, 1595, 1596, 1597, 1629, 1638, 1594, 1569, 1570, + /* 340 */ 1600, 1568, 1610, 1601, 1611, 1603, 1643, 1651, 1562, 1571, + /* 350 */ 1655, 1659, 1640, 1663, 1666, 1664, 1667, 1641, 1650, 1652, + /* 360 */ 1653, 1647, 1656, 1657, 1658, 1668, 1672, 1681, 1649, 1682, + /* 370 */ 1683, 1573, 1582, 1607, 1615, 1685, 1702, 1586, 1587, 1642, + /* 380 */ 1646, 1673, 1675, 1636, 1714, 1637, 1677, 1674, 1678, 1694, + /* 390 */ 1719, 1734, 1735, 1746, 1747, 1750, 1633, 1661, 1686, 1738, + /* 400 */ 1728, 1733, 1736, 1737, 1740, 1726, 1730, 1742, 1743, 1748, + /* 410 */ 1753, }; static const YYACTIONTYPE yy_default[] = { - /* 0 */ 1639, 1639, 1639, 1469, 1236, 1347, 1236, 1236, 1236, 1469, - /* 10 */ 1469, 1469, 1236, 1377, 1377, 1522, 1269, 1236, 1236, 1236, - /* 20 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1468, 1236, 1236, - /* 30 */ 1236, 1236, 1555, 1555, 1236, 1236, 1236, 1236, 1236, 1236, - /* 40 */ 1236, 1236, 1386, 1236, 1393, 1236, 1236, 1236, 1236, 1236, - /* 50 */ 1470, 1471, 1236, 1236, 1236, 1521, 1523, 1486, 1400, 1399, - /* 60 */ 1398, 1397, 1504, 1365, 1391, 1384, 1388, 1465, 1466, 1464, - /* 70 */ 1617, 1471, 1470, 1236, 1387, 1433, 1449, 1432, 1236, 1236, - /* 80 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, - /* 90 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, - /* 100 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, - /* 110 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, - /* 120 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, - /* 130 */ 1441, 1448, 1447, 1446, 1455, 1445, 1442, 1435, 1434, 1436, - /* 140 */ 1437, 1236, 1236, 1260, 1236, 1236, 1257, 1311, 1236, 1236, - /* 150 */ 1236, 1236, 1236, 1541, 1540, 1236, 1438, 1236, 1269, 1427, - /* 160 */ 1426, 1452, 1439, 1451, 1450, 1529, 1591, 1590, 1487, 1236, - /* 170 */ 1236, 1236, 1236, 1236, 1236, 1555, 1236, 1236, 1236, 1236, - /* 180 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, - /* 190 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1367, - /* 200 */ 1555, 1555, 1236, 1269, 1555, 1555, 1368, 1368, 1265, 1265, - /* 210 */ 1371, 1236, 1536, 1338, 1338, 1338, 1338, 1347, 1338, 1236, - /* 220 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, - /* 230 */ 1236, 1236, 1236, 1236, 1526, 1524, 1236, 1236, 1236, 1236, - /* 240 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, - /* 250 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, - /* 260 */ 1236, 1236, 1236, 1343, 1236, 1236, 1236, 1236, 1236, 1236, - /* 270 */ 1236, 1236, 1236, 1236, 1236, 1584, 1236, 1499, 1325, 1343, - /* 280 */ 1343, 1343, 1343, 1345, 1326, 1324, 1337, 1270, 1243, 1631, - /* 290 */ 1403, 1392, 1344, 1392, 1628, 1390, 1403, 1403, 1390, 1403, - /* 300 */ 1344, 1628, 1286, 1606, 1281, 1377, 1377, 1377, 1367, 1367, - /* 310 */ 1367, 1367, 1371, 1371, 1467, 1344, 1337, 1236, 1631, 1631, - /* 320 */ 1353, 1353, 1630, 1630, 1353, 1487, 1614, 1412, 1314, 1320, - /* 330 */ 1320, 1320, 1320, 1353, 1254, 1390, 1614, 1614, 1390, 1412, - /* 340 */ 1314, 1390, 1314, 1390, 1353, 1254, 1503, 1625, 1353, 1254, - /* 350 */ 1477, 1353, 1254, 1353, 1254, 1477, 1312, 1312, 1312, 1301, - /* 360 */ 1236, 1236, 1477, 1312, 1286, 1312, 1301, 1312, 1312, 1573, - /* 370 */ 1236, 1481, 1481, 1477, 1353, 1565, 1565, 1380, 1380, 1385, - /* 380 */ 1371, 1472, 1353, 1236, 1385, 1383, 1381, 1390, 1304, 1587, - /* 390 */ 1587, 1583, 1583, 1583, 1636, 1636, 1536, 1599, 1269, 1269, - /* 400 */ 1269, 1269, 1599, 1288, 1288, 1270, 1270, 1269, 1599, 1236, - /* 410 */ 1236, 1236, 1236, 1236, 1236, 1594, 1236, 1531, 1488, 1357, - /* 420 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, - /* 430 */ 1236, 1236, 1236, 1236, 1542, 1236, 1236, 1236, 1236, 1236, - /* 440 */ 1236, 1236, 1236, 1236, 1236, 1417, 1236, 1239, 1533, 1236, - /* 450 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1394, 1395, 1358, - /* 460 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1409, 1236, 1236, - /* 470 */ 1236, 1404, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, - /* 480 */ 1627, 1236, 1236, 1236, 1236, 1236, 1236, 1502, 1501, 1236, - /* 490 */ 1236, 1355, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, - /* 500 */ 1236, 1236, 1236, 1236, 1236, 1284, 1236, 1236, 1236, 1236, - /* 510 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, - /* 520 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1382, - /* 530 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, - /* 540 */ 1236, 1236, 1236, 1236, 1570, 1372, 1236, 1236, 1236, 1236, - /* 550 */ 1618, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, - /* 560 */ 1236, 1236, 1236, 1236, 1236, 1610, 1328, 1418, 1236, 1421, - /* 570 */ 1258, 1236, 1248, 1236, 1236, + /* 0 */ 1648, 1648, 1648, 1478, 1243, 1354, 1243, 1243, 1243, 1478, + /* 10 */ 1478, 1478, 1243, 1384, 1384, 1531, 1276, 1243, 1243, 1243, + /* 20 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1477, 1243, + /* 30 */ 1243, 1243, 1243, 1564, 1564, 1243, 1243, 1243, 1243, 1243, + /* 40 */ 1243, 1243, 1243, 1393, 1243, 1400, 1243, 1243, 1243, 1243, + /* 50 */ 1243, 1479, 1480, 1243, 1243, 1243, 1530, 1532, 1495, 1407, + /* 60 */ 1406, 1405, 1404, 1513, 1372, 1398, 1391, 1395, 1474, 1475, + /* 70 */ 1473, 1626, 1480, 1479, 1243, 1394, 1442, 1458, 1441, 1243, + /* 80 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, + /* 90 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, + /* 100 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, + /* 110 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, + /* 120 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, + /* 130 */ 1243, 1450, 1457, 1456, 1455, 1464, 1454, 1451, 1444, 1443, + /* 140 */ 1445, 1446, 1243, 1243, 1267, 1243, 1243, 1264, 1318, 1243, + /* 150 */ 1243, 1243, 1243, 1243, 1550, 1549, 1243, 1447, 1243, 1276, + /* 160 */ 1435, 1434, 1433, 1461, 1448, 1460, 1459, 1538, 1600, 1599, + /* 170 */ 1496, 1243, 1243, 1243, 1243, 1243, 1243, 1564, 1243, 1243, + /* 180 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, + /* 190 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, + /* 200 */ 1243, 1374, 1564, 1564, 1243, 1276, 1564, 1564, 1375, 1375, + /* 210 */ 1272, 1272, 1378, 1243, 1545, 1345, 1345, 1345, 1345, 1354, + /* 220 */ 1345, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, + /* 230 */ 1243, 1243, 1243, 1243, 1243, 1243, 1535, 1533, 1243, 1243, + /* 240 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, + /* 250 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, + /* 260 */ 1243, 1243, 1243, 1243, 1243, 1350, 1243, 1243, 1243, 1243, + /* 270 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1593, 1243, 1508, + /* 280 */ 1332, 1350, 1350, 1350, 1350, 1352, 1333, 1331, 1344, 1277, + /* 290 */ 1250, 1640, 1410, 1399, 1351, 1399, 1637, 1397, 1410, 1410, + /* 300 */ 1397, 1410, 1351, 1637, 1293, 1615, 1288, 1384, 1384, 1384, + /* 310 */ 1374, 1374, 1374, 1374, 1378, 1378, 1476, 1351, 1344, 1243, + /* 320 */ 1640, 1640, 1360, 1360, 1639, 1639, 1360, 1496, 1623, 1419, + /* 330 */ 1321, 1327, 1327, 1327, 1327, 1360, 1261, 1397, 1623, 1623, + /* 340 */ 1397, 1419, 1321, 1397, 1321, 1397, 1360, 1261, 1512, 1634, + /* 350 */ 1360, 1261, 1486, 1360, 1261, 1360, 1261, 1486, 1319, 1319, + /* 360 */ 1319, 1308, 1243, 1243, 1486, 1319, 1293, 1319, 1308, 1319, + /* 370 */ 1319, 1582, 1243, 1490, 1490, 1486, 1360, 1574, 1574, 1387, + /* 380 */ 1387, 1392, 1378, 1481, 1360, 1243, 1392, 1390, 1388, 1397, + /* 390 */ 1311, 1596, 1596, 1592, 1592, 1592, 1645, 1645, 1545, 1608, + /* 400 */ 1276, 1276, 1276, 1276, 1608, 1295, 1295, 1277, 1277, 1276, + /* 410 */ 1608, 1243, 1243, 1243, 1243, 1243, 1243, 1603, 1243, 1540, + /* 420 */ 1497, 1364, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, + /* 430 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1551, 1243, + /* 440 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1424, + /* 450 */ 1243, 1246, 1542, 1243, 1243, 1243, 1243, 1243, 1243, 1243, + /* 460 */ 1243, 1401, 1402, 1365, 1243, 1243, 1243, 1243, 1243, 1243, + /* 470 */ 1243, 1416, 1243, 1243, 1243, 1411, 1243, 1243, 1243, 1243, + /* 480 */ 1243, 1243, 1243, 1243, 1636, 1243, 1243, 1243, 1243, 1243, + /* 490 */ 1243, 1511, 1510, 1243, 1243, 1362, 1243, 1243, 1243, 1243, + /* 500 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1291, + /* 510 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, + /* 520 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, + /* 530 */ 1243, 1243, 1243, 1389, 1243, 1243, 1243, 1243, 1243, 1243, + /* 540 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1579, 1379, + /* 550 */ 1243, 1243, 1243, 1243, 1627, 1243, 1243, 1243, 1243, 1243, + /* 560 */ 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1619, + /* 570 */ 1335, 1425, 1243, 1428, 1265, 1243, 1255, 1243, 1243, }; /********** End of lemon-generated parsing tables *****************************/ @@ -171229,221 +172915,223 @@ static const char *const yyRuleName[] = { /* 185 */ "expr ::= expr COLLATE ID|STRING", /* 186 */ "expr ::= CAST LP expr AS typetoken RP", /* 187 */ "expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP", - /* 188 */ "expr ::= ID|INDEXED|JOIN_KW LP STAR RP", - /* 189 */ "expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP filter_over", - /* 190 */ "expr ::= ID|INDEXED|JOIN_KW LP STAR RP filter_over", - /* 191 */ "term ::= CTIME_KW", - /* 192 */ "expr ::= LP nexprlist COMMA expr RP", - /* 193 */ "expr ::= expr AND expr", - /* 194 */ "expr ::= expr OR expr", - /* 195 */ "expr ::= expr LT|GT|GE|LE expr", - /* 196 */ "expr ::= expr EQ|NE expr", - /* 197 */ "expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr", - /* 198 */ "expr ::= expr PLUS|MINUS expr", - /* 199 */ "expr ::= expr STAR|SLASH|REM expr", - /* 200 */ "expr ::= expr CONCAT expr", - /* 201 */ "likeop ::= NOT LIKE_KW|MATCH", - /* 202 */ "expr ::= expr likeop expr", - /* 203 */ "expr ::= expr likeop expr ESCAPE expr", - /* 204 */ "expr ::= expr ISNULL|NOTNULL", - /* 205 */ "expr ::= expr NOT NULL", - /* 206 */ "expr ::= expr IS expr", - /* 207 */ "expr ::= expr IS NOT expr", - /* 208 */ "expr ::= expr IS NOT DISTINCT FROM expr", - /* 209 */ "expr ::= expr IS DISTINCT FROM expr", - /* 210 */ "expr ::= NOT expr", - /* 211 */ "expr ::= BITNOT expr", - /* 212 */ "expr ::= PLUS|MINUS expr", - /* 213 */ "expr ::= expr PTR expr", - /* 214 */ "between_op ::= BETWEEN", - /* 215 */ "between_op ::= NOT BETWEEN", - /* 216 */ "expr ::= expr between_op expr AND expr", - /* 217 */ "in_op ::= IN", - /* 218 */ "in_op ::= NOT IN", - /* 219 */ "expr ::= expr in_op LP exprlist RP", - /* 220 */ "expr ::= LP select RP", - /* 221 */ "expr ::= expr in_op LP select RP", - /* 222 */ "expr ::= expr in_op nm dbnm paren_exprlist", - /* 223 */ "expr ::= EXISTS LP select RP", - /* 224 */ "expr ::= CASE case_operand case_exprlist case_else END", - /* 225 */ "case_exprlist ::= case_exprlist WHEN expr THEN expr", - /* 226 */ "case_exprlist ::= WHEN expr THEN expr", - /* 227 */ "case_else ::= ELSE expr", - /* 228 */ "case_else ::=", - /* 229 */ "case_operand ::=", - /* 230 */ "exprlist ::=", - /* 231 */ "nexprlist ::= nexprlist COMMA expr", - /* 232 */ "nexprlist ::= expr", - /* 233 */ "paren_exprlist ::=", - /* 234 */ "paren_exprlist ::= LP exprlist RP", - /* 235 */ "cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt", - /* 236 */ "uniqueflag ::= UNIQUE", - /* 237 */ "uniqueflag ::=", - /* 238 */ "eidlist_opt ::=", - /* 239 */ "eidlist_opt ::= LP eidlist RP", - /* 240 */ "eidlist ::= eidlist COMMA nm collate sortorder", - /* 241 */ "eidlist ::= nm collate sortorder", - /* 242 */ "collate ::=", - /* 243 */ "collate ::= COLLATE ID|STRING", - /* 244 */ "cmd ::= DROP INDEX ifexists fullname", - /* 245 */ "cmd ::= VACUUM vinto", - /* 246 */ "cmd ::= VACUUM nm vinto", - /* 247 */ "vinto ::= INTO expr", - /* 248 */ "vinto ::=", - /* 249 */ "cmd ::= PRAGMA nm dbnm", - /* 250 */ "cmd ::= PRAGMA nm dbnm EQ nmnum", - /* 251 */ "cmd ::= PRAGMA nm dbnm LP nmnum RP", - /* 252 */ "cmd ::= PRAGMA nm dbnm EQ minus_num", - /* 253 */ "cmd ::= PRAGMA nm dbnm LP minus_num RP", - /* 254 */ "plus_num ::= PLUS INTEGER|FLOAT", - /* 255 */ "minus_num ::= MINUS INTEGER|FLOAT", - /* 256 */ "cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END", - /* 257 */ "trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause", - /* 258 */ "trigger_time ::= BEFORE|AFTER", - /* 259 */ "trigger_time ::= INSTEAD OF", - /* 260 */ "trigger_time ::=", - /* 261 */ "trigger_event ::= DELETE|INSERT", - /* 262 */ "trigger_event ::= UPDATE", - /* 263 */ "trigger_event ::= UPDATE OF idlist", - /* 264 */ "when_clause ::=", - /* 265 */ "when_clause ::= WHEN expr", - /* 266 */ "trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI", - /* 267 */ "trigger_cmd_list ::= trigger_cmd SEMI", - /* 268 */ "trnm ::= nm DOT nm", - /* 269 */ "tridxby ::= INDEXED BY nm", - /* 270 */ "tridxby ::= NOT INDEXED", - /* 271 */ "trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt", - /* 272 */ "trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt", - /* 273 */ "trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt", - /* 274 */ "trigger_cmd ::= scanpt select scanpt", - /* 275 */ "expr ::= RAISE LP IGNORE RP", - /* 276 */ "expr ::= RAISE LP raisetype COMMA nm RP", - /* 277 */ "raisetype ::= ROLLBACK", - /* 278 */ "raisetype ::= ABORT", - /* 279 */ "raisetype ::= FAIL", - /* 280 */ "cmd ::= DROP TRIGGER ifexists fullname", - /* 281 */ "cmd ::= ATTACH database_kw_opt expr AS expr key_opt", - /* 282 */ "cmd ::= DETACH database_kw_opt expr", - /* 283 */ "key_opt ::=", - /* 284 */ "key_opt ::= KEY expr", - /* 285 */ "cmd ::= REINDEX", - /* 286 */ "cmd ::= REINDEX nm dbnm", - /* 287 */ "cmd ::= ANALYZE", - /* 288 */ "cmd ::= ANALYZE nm dbnm", - /* 289 */ "cmd ::= ALTER TABLE fullname RENAME TO nm", - /* 290 */ "cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist", - /* 291 */ "cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm", - /* 292 */ "add_column_fullname ::= fullname", - /* 293 */ "cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm", - /* 294 */ "cmd ::= create_vtab", - /* 295 */ "cmd ::= create_vtab LP vtabarglist RP", - /* 296 */ "create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm", - /* 297 */ "vtabarg ::=", - /* 298 */ "vtabargtoken ::= ANY", - /* 299 */ "vtabargtoken ::= lp anylist RP", - /* 300 */ "lp ::= LP", - /* 301 */ "with ::= WITH wqlist", - /* 302 */ "with ::= WITH RECURSIVE wqlist", - /* 303 */ "wqas ::= AS", - /* 304 */ "wqas ::= AS MATERIALIZED", - /* 305 */ "wqas ::= AS NOT MATERIALIZED", - /* 306 */ "wqitem ::= nm eidlist_opt wqas LP select RP", - /* 307 */ "wqlist ::= wqitem", - /* 308 */ "wqlist ::= wqlist COMMA wqitem", - /* 309 */ "windowdefn_list ::= windowdefn_list COMMA windowdefn", - /* 310 */ "windowdefn ::= nm AS LP window RP", - /* 311 */ "window ::= PARTITION BY nexprlist orderby_opt frame_opt", - /* 312 */ "window ::= nm PARTITION BY nexprlist orderby_opt frame_opt", - /* 313 */ "window ::= ORDER BY sortlist frame_opt", - /* 314 */ "window ::= nm ORDER BY sortlist frame_opt", - /* 315 */ "window ::= nm frame_opt", - /* 316 */ "frame_opt ::=", - /* 317 */ "frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt", - /* 318 */ "frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt", - /* 319 */ "range_or_rows ::= RANGE|ROWS|GROUPS", - /* 320 */ "frame_bound_s ::= frame_bound", - /* 321 */ "frame_bound_s ::= UNBOUNDED PRECEDING", - /* 322 */ "frame_bound_e ::= frame_bound", - /* 323 */ "frame_bound_e ::= UNBOUNDED FOLLOWING", - /* 324 */ "frame_bound ::= expr PRECEDING|FOLLOWING", - /* 325 */ "frame_bound ::= CURRENT ROW", - /* 326 */ "frame_exclude_opt ::=", - /* 327 */ "frame_exclude_opt ::= EXCLUDE frame_exclude", - /* 328 */ "frame_exclude ::= NO OTHERS", - /* 329 */ "frame_exclude ::= CURRENT ROW", - /* 330 */ "frame_exclude ::= GROUP|TIES", - /* 331 */ "window_clause ::= WINDOW windowdefn_list", - /* 332 */ "filter_over ::= filter_clause over_clause", - /* 333 */ "filter_over ::= over_clause", - /* 334 */ "filter_over ::= filter_clause", - /* 335 */ "over_clause ::= OVER LP window RP", - /* 336 */ "over_clause ::= OVER nm", - /* 337 */ "filter_clause ::= FILTER LP WHERE expr RP", - /* 338 */ "input ::= cmdlist", - /* 339 */ "cmdlist ::= cmdlist ecmd", - /* 340 */ "cmdlist ::= ecmd", - /* 341 */ "ecmd ::= SEMI", - /* 342 */ "ecmd ::= cmdx SEMI", - /* 343 */ "ecmd ::= explain cmdx SEMI", - /* 344 */ "trans_opt ::=", - /* 345 */ "trans_opt ::= TRANSACTION", - /* 346 */ "trans_opt ::= TRANSACTION nm", - /* 347 */ "savepoint_opt ::= SAVEPOINT", - /* 348 */ "savepoint_opt ::=", - /* 349 */ "cmd ::= create_table create_table_args", - /* 350 */ "table_option_set ::= table_option", - /* 351 */ "columnlist ::= columnlist COMMA columnname carglist", - /* 352 */ "columnlist ::= columnname carglist", - /* 353 */ "nm ::= ID|INDEXED|JOIN_KW", - /* 354 */ "nm ::= STRING", - /* 355 */ "typetoken ::= typename", - /* 356 */ "typename ::= ID|STRING", - /* 357 */ "signed ::= plus_num", - /* 358 */ "signed ::= minus_num", - /* 359 */ "carglist ::= carglist ccons", - /* 360 */ "carglist ::=", - /* 361 */ "ccons ::= NULL onconf", - /* 362 */ "ccons ::= GENERATED ALWAYS AS generated", - /* 363 */ "ccons ::= AS generated", - /* 364 */ "conslist_opt ::= COMMA conslist", - /* 365 */ "conslist ::= conslist tconscomma tcons", - /* 366 */ "conslist ::= tcons", - /* 367 */ "tconscomma ::=", - /* 368 */ "defer_subclause_opt ::= defer_subclause", - /* 369 */ "resolvetype ::= raisetype", - /* 370 */ "selectnowith ::= oneselect", - /* 371 */ "oneselect ::= values", - /* 372 */ "sclp ::= selcollist COMMA", - /* 373 */ "as ::= ID|STRING", - /* 374 */ "indexed_opt ::= indexed_by", - /* 375 */ "returning ::=", - /* 376 */ "expr ::= term", - /* 377 */ "likeop ::= LIKE_KW|MATCH", - /* 378 */ "case_operand ::= expr", - /* 379 */ "exprlist ::= nexprlist", - /* 380 */ "nmnum ::= plus_num", - /* 381 */ "nmnum ::= nm", - /* 382 */ "nmnum ::= ON", - /* 383 */ "nmnum ::= DELETE", - /* 384 */ "nmnum ::= DEFAULT", - /* 385 */ "plus_num ::= INTEGER|FLOAT", - /* 386 */ "foreach_clause ::=", - /* 387 */ "foreach_clause ::= FOR EACH ROW", - /* 388 */ "trnm ::= nm", - /* 389 */ "tridxby ::=", - /* 390 */ "database_kw_opt ::= DATABASE", - /* 391 */ "database_kw_opt ::=", - /* 392 */ "kwcolumn_opt ::=", - /* 393 */ "kwcolumn_opt ::= COLUMNKW", - /* 394 */ "vtabarglist ::= vtabarg", - /* 395 */ "vtabarglist ::= vtabarglist COMMA vtabarg", - /* 396 */ "vtabarg ::= vtabarg vtabargtoken", - /* 397 */ "anylist ::=", - /* 398 */ "anylist ::= anylist LP anylist RP", - /* 399 */ "anylist ::= anylist ANY", - /* 400 */ "with ::=", - /* 401 */ "windowdefn_list ::= windowdefn", - /* 402 */ "window ::= frame_opt", + /* 188 */ "expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP", + /* 189 */ "expr ::= ID|INDEXED|JOIN_KW LP STAR RP", + /* 190 */ "expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP filter_over", + /* 191 */ "expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP filter_over", + /* 192 */ "expr ::= ID|INDEXED|JOIN_KW LP STAR RP filter_over", + /* 193 */ "term ::= CTIME_KW", + /* 194 */ "expr ::= LP nexprlist COMMA expr RP", + /* 195 */ "expr ::= expr AND expr", + /* 196 */ "expr ::= expr OR expr", + /* 197 */ "expr ::= expr LT|GT|GE|LE expr", + /* 198 */ "expr ::= expr EQ|NE expr", + /* 199 */ "expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr", + /* 200 */ "expr ::= expr PLUS|MINUS expr", + /* 201 */ "expr ::= expr STAR|SLASH|REM expr", + /* 202 */ "expr ::= expr CONCAT expr", + /* 203 */ "likeop ::= NOT LIKE_KW|MATCH", + /* 204 */ "expr ::= expr likeop expr", + /* 205 */ "expr ::= expr likeop expr ESCAPE expr", + /* 206 */ "expr ::= expr ISNULL|NOTNULL", + /* 207 */ "expr ::= expr NOT NULL", + /* 208 */ "expr ::= expr IS expr", + /* 209 */ "expr ::= expr IS NOT expr", + /* 210 */ "expr ::= expr IS NOT DISTINCT FROM expr", + /* 211 */ "expr ::= expr IS DISTINCT FROM expr", + /* 212 */ "expr ::= NOT expr", + /* 213 */ "expr ::= BITNOT expr", + /* 214 */ "expr ::= PLUS|MINUS expr", + /* 215 */ "expr ::= expr PTR expr", + /* 216 */ "between_op ::= BETWEEN", + /* 217 */ "between_op ::= NOT BETWEEN", + /* 218 */ "expr ::= expr between_op expr AND expr", + /* 219 */ "in_op ::= IN", + /* 220 */ "in_op ::= NOT IN", + /* 221 */ "expr ::= expr in_op LP exprlist RP", + /* 222 */ "expr ::= LP select RP", + /* 223 */ "expr ::= expr in_op LP select RP", + /* 224 */ "expr ::= expr in_op nm dbnm paren_exprlist", + /* 225 */ "expr ::= EXISTS LP select RP", + /* 226 */ "expr ::= CASE case_operand case_exprlist case_else END", + /* 227 */ "case_exprlist ::= case_exprlist WHEN expr THEN expr", + /* 228 */ "case_exprlist ::= WHEN expr THEN expr", + /* 229 */ "case_else ::= ELSE expr", + /* 230 */ "case_else ::=", + /* 231 */ "case_operand ::=", + /* 232 */ "exprlist ::=", + /* 233 */ "nexprlist ::= nexprlist COMMA expr", + /* 234 */ "nexprlist ::= expr", + /* 235 */ "paren_exprlist ::=", + /* 236 */ "paren_exprlist ::= LP exprlist RP", + /* 237 */ "cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt", + /* 238 */ "uniqueflag ::= UNIQUE", + /* 239 */ "uniqueflag ::=", + /* 240 */ "eidlist_opt ::=", + /* 241 */ "eidlist_opt ::= LP eidlist RP", + /* 242 */ "eidlist ::= eidlist COMMA nm collate sortorder", + /* 243 */ "eidlist ::= nm collate sortorder", + /* 244 */ "collate ::=", + /* 245 */ "collate ::= COLLATE ID|STRING", + /* 246 */ "cmd ::= DROP INDEX ifexists fullname", + /* 247 */ "cmd ::= VACUUM vinto", + /* 248 */ "cmd ::= VACUUM nm vinto", + /* 249 */ "vinto ::= INTO expr", + /* 250 */ "vinto ::=", + /* 251 */ "cmd ::= PRAGMA nm dbnm", + /* 252 */ "cmd ::= PRAGMA nm dbnm EQ nmnum", + /* 253 */ "cmd ::= PRAGMA nm dbnm LP nmnum RP", + /* 254 */ "cmd ::= PRAGMA nm dbnm EQ minus_num", + /* 255 */ "cmd ::= PRAGMA nm dbnm LP minus_num RP", + /* 256 */ "plus_num ::= PLUS INTEGER|FLOAT", + /* 257 */ "minus_num ::= MINUS INTEGER|FLOAT", + /* 258 */ "cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END", + /* 259 */ "trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause", + /* 260 */ "trigger_time ::= BEFORE|AFTER", + /* 261 */ "trigger_time ::= INSTEAD OF", + /* 262 */ "trigger_time ::=", + /* 263 */ "trigger_event ::= DELETE|INSERT", + /* 264 */ "trigger_event ::= UPDATE", + /* 265 */ "trigger_event ::= UPDATE OF idlist", + /* 266 */ "when_clause ::=", + /* 267 */ "when_clause ::= WHEN expr", + /* 268 */ "trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI", + /* 269 */ "trigger_cmd_list ::= trigger_cmd SEMI", + /* 270 */ "trnm ::= nm DOT nm", + /* 271 */ "tridxby ::= INDEXED BY nm", + /* 272 */ "tridxby ::= NOT INDEXED", + /* 273 */ "trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt", + /* 274 */ "trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt", + /* 275 */ "trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt", + /* 276 */ "trigger_cmd ::= scanpt select scanpt", + /* 277 */ "expr ::= RAISE LP IGNORE RP", + /* 278 */ "expr ::= RAISE LP raisetype COMMA nm RP", + /* 279 */ "raisetype ::= ROLLBACK", + /* 280 */ "raisetype ::= ABORT", + /* 281 */ "raisetype ::= FAIL", + /* 282 */ "cmd ::= DROP TRIGGER ifexists fullname", + /* 283 */ "cmd ::= ATTACH database_kw_opt expr AS expr key_opt", + /* 284 */ "cmd ::= DETACH database_kw_opt expr", + /* 285 */ "key_opt ::=", + /* 286 */ "key_opt ::= KEY expr", + /* 287 */ "cmd ::= REINDEX", + /* 288 */ "cmd ::= REINDEX nm dbnm", + /* 289 */ "cmd ::= ANALYZE", + /* 290 */ "cmd ::= ANALYZE nm dbnm", + /* 291 */ "cmd ::= ALTER TABLE fullname RENAME TO nm", + /* 292 */ "cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist", + /* 293 */ "cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm", + /* 294 */ "add_column_fullname ::= fullname", + /* 295 */ "cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm", + /* 296 */ "cmd ::= create_vtab", + /* 297 */ "cmd ::= create_vtab LP vtabarglist RP", + /* 298 */ "create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm", + /* 299 */ "vtabarg ::=", + /* 300 */ "vtabargtoken ::= ANY", + /* 301 */ "vtabargtoken ::= lp anylist RP", + /* 302 */ "lp ::= LP", + /* 303 */ "with ::= WITH wqlist", + /* 304 */ "with ::= WITH RECURSIVE wqlist", + /* 305 */ "wqas ::= AS", + /* 306 */ "wqas ::= AS MATERIALIZED", + /* 307 */ "wqas ::= AS NOT MATERIALIZED", + /* 308 */ "wqitem ::= nm eidlist_opt wqas LP select RP", + /* 309 */ "wqlist ::= wqitem", + /* 310 */ "wqlist ::= wqlist COMMA wqitem", + /* 311 */ "windowdefn_list ::= windowdefn_list COMMA windowdefn", + /* 312 */ "windowdefn ::= nm AS LP window RP", + /* 313 */ "window ::= PARTITION BY nexprlist orderby_opt frame_opt", + /* 314 */ "window ::= nm PARTITION BY nexprlist orderby_opt frame_opt", + /* 315 */ "window ::= ORDER BY sortlist frame_opt", + /* 316 */ "window ::= nm ORDER BY sortlist frame_opt", + /* 317 */ "window ::= nm frame_opt", + /* 318 */ "frame_opt ::=", + /* 319 */ "frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt", + /* 320 */ "frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt", + /* 321 */ "range_or_rows ::= RANGE|ROWS|GROUPS", + /* 322 */ "frame_bound_s ::= frame_bound", + /* 323 */ "frame_bound_s ::= UNBOUNDED PRECEDING", + /* 324 */ "frame_bound_e ::= frame_bound", + /* 325 */ "frame_bound_e ::= UNBOUNDED FOLLOWING", + /* 326 */ "frame_bound ::= expr PRECEDING|FOLLOWING", + /* 327 */ "frame_bound ::= CURRENT ROW", + /* 328 */ "frame_exclude_opt ::=", + /* 329 */ "frame_exclude_opt ::= EXCLUDE frame_exclude", + /* 330 */ "frame_exclude ::= NO OTHERS", + /* 331 */ "frame_exclude ::= CURRENT ROW", + /* 332 */ "frame_exclude ::= GROUP|TIES", + /* 333 */ "window_clause ::= WINDOW windowdefn_list", + /* 334 */ "filter_over ::= filter_clause over_clause", + /* 335 */ "filter_over ::= over_clause", + /* 336 */ "filter_over ::= filter_clause", + /* 337 */ "over_clause ::= OVER LP window RP", + /* 338 */ "over_clause ::= OVER nm", + /* 339 */ "filter_clause ::= FILTER LP WHERE expr RP", + /* 340 */ "input ::= cmdlist", + /* 341 */ "cmdlist ::= cmdlist ecmd", + /* 342 */ "cmdlist ::= ecmd", + /* 343 */ "ecmd ::= SEMI", + /* 344 */ "ecmd ::= cmdx SEMI", + /* 345 */ "ecmd ::= explain cmdx SEMI", + /* 346 */ "trans_opt ::=", + /* 347 */ "trans_opt ::= TRANSACTION", + /* 348 */ "trans_opt ::= TRANSACTION nm", + /* 349 */ "savepoint_opt ::= SAVEPOINT", + /* 350 */ "savepoint_opt ::=", + /* 351 */ "cmd ::= create_table create_table_args", + /* 352 */ "table_option_set ::= table_option", + /* 353 */ "columnlist ::= columnlist COMMA columnname carglist", + /* 354 */ "columnlist ::= columnname carglist", + /* 355 */ "nm ::= ID|INDEXED|JOIN_KW", + /* 356 */ "nm ::= STRING", + /* 357 */ "typetoken ::= typename", + /* 358 */ "typename ::= ID|STRING", + /* 359 */ "signed ::= plus_num", + /* 360 */ "signed ::= minus_num", + /* 361 */ "carglist ::= carglist ccons", + /* 362 */ "carglist ::=", + /* 363 */ "ccons ::= NULL onconf", + /* 364 */ "ccons ::= GENERATED ALWAYS AS generated", + /* 365 */ "ccons ::= AS generated", + /* 366 */ "conslist_opt ::= COMMA conslist", + /* 367 */ "conslist ::= conslist tconscomma tcons", + /* 368 */ "conslist ::= tcons", + /* 369 */ "tconscomma ::=", + /* 370 */ "defer_subclause_opt ::= defer_subclause", + /* 371 */ "resolvetype ::= raisetype", + /* 372 */ "selectnowith ::= oneselect", + /* 373 */ "oneselect ::= values", + /* 374 */ "sclp ::= selcollist COMMA", + /* 375 */ "as ::= ID|STRING", + /* 376 */ "indexed_opt ::= indexed_by", + /* 377 */ "returning ::=", + /* 378 */ "expr ::= term", + /* 379 */ "likeop ::= LIKE_KW|MATCH", + /* 380 */ "case_operand ::= expr", + /* 381 */ "exprlist ::= nexprlist", + /* 382 */ "nmnum ::= plus_num", + /* 383 */ "nmnum ::= nm", + /* 384 */ "nmnum ::= ON", + /* 385 */ "nmnum ::= DELETE", + /* 386 */ "nmnum ::= DEFAULT", + /* 387 */ "plus_num ::= INTEGER|FLOAT", + /* 388 */ "foreach_clause ::=", + /* 389 */ "foreach_clause ::= FOR EACH ROW", + /* 390 */ "trnm ::= nm", + /* 391 */ "tridxby ::=", + /* 392 */ "database_kw_opt ::= DATABASE", + /* 393 */ "database_kw_opt ::=", + /* 394 */ "kwcolumn_opt ::=", + /* 395 */ "kwcolumn_opt ::= COLUMNKW", + /* 396 */ "vtabarglist ::= vtabarg", + /* 397 */ "vtabarglist ::= vtabarglist COMMA vtabarg", + /* 398 */ "vtabarg ::= vtabarg vtabargtoken", + /* 399 */ "anylist ::=", + /* 400 */ "anylist ::= anylist LP anylist RP", + /* 401 */ "anylist ::= anylist ANY", + /* 402 */ "with ::=", + /* 403 */ "windowdefn_list ::= windowdefn", + /* 404 */ "window ::= frame_opt", }; #endif /* NDEBUG */ @@ -172138,221 +173826,223 @@ static const YYCODETYPE yyRuleInfoLhs[] = { 217, /* (185) expr ::= expr COLLATE ID|STRING */ 217, /* (186) expr ::= CAST LP expr AS typetoken RP */ 217, /* (187) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP */ - 217, /* (188) expr ::= ID|INDEXED|JOIN_KW LP STAR RP */ - 217, /* (189) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP filter_over */ - 217, /* (190) expr ::= ID|INDEXED|JOIN_KW LP STAR RP filter_over */ - 216, /* (191) term ::= CTIME_KW */ - 217, /* (192) expr ::= LP nexprlist COMMA expr RP */ - 217, /* (193) expr ::= expr AND expr */ - 217, /* (194) expr ::= expr OR expr */ - 217, /* (195) expr ::= expr LT|GT|GE|LE expr */ - 217, /* (196) expr ::= expr EQ|NE expr */ - 217, /* (197) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ - 217, /* (198) expr ::= expr PLUS|MINUS expr */ - 217, /* (199) expr ::= expr STAR|SLASH|REM expr */ - 217, /* (200) expr ::= expr CONCAT expr */ - 274, /* (201) likeop ::= NOT LIKE_KW|MATCH */ - 217, /* (202) expr ::= expr likeop expr */ - 217, /* (203) expr ::= expr likeop expr ESCAPE expr */ - 217, /* (204) expr ::= expr ISNULL|NOTNULL */ - 217, /* (205) expr ::= expr NOT NULL */ - 217, /* (206) expr ::= expr IS expr */ - 217, /* (207) expr ::= expr IS NOT expr */ - 217, /* (208) expr ::= expr IS NOT DISTINCT FROM expr */ - 217, /* (209) expr ::= expr IS DISTINCT FROM expr */ - 217, /* (210) expr ::= NOT expr */ - 217, /* (211) expr ::= BITNOT expr */ - 217, /* (212) expr ::= PLUS|MINUS expr */ - 217, /* (213) expr ::= expr PTR expr */ - 275, /* (214) between_op ::= BETWEEN */ - 275, /* (215) between_op ::= NOT BETWEEN */ - 217, /* (216) expr ::= expr between_op expr AND expr */ - 276, /* (217) in_op ::= IN */ - 276, /* (218) in_op ::= NOT IN */ - 217, /* (219) expr ::= expr in_op LP exprlist RP */ - 217, /* (220) expr ::= LP select RP */ - 217, /* (221) expr ::= expr in_op LP select RP */ - 217, /* (222) expr ::= expr in_op nm dbnm paren_exprlist */ - 217, /* (223) expr ::= EXISTS LP select RP */ - 217, /* (224) expr ::= CASE case_operand case_exprlist case_else END */ - 279, /* (225) case_exprlist ::= case_exprlist WHEN expr THEN expr */ - 279, /* (226) case_exprlist ::= WHEN expr THEN expr */ - 280, /* (227) case_else ::= ELSE expr */ - 280, /* (228) case_else ::= */ - 278, /* (229) case_operand ::= */ - 261, /* (230) exprlist ::= */ - 253, /* (231) nexprlist ::= nexprlist COMMA expr */ - 253, /* (232) nexprlist ::= expr */ - 277, /* (233) paren_exprlist ::= */ - 277, /* (234) paren_exprlist ::= LP exprlist RP */ - 190, /* (235) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ - 281, /* (236) uniqueflag ::= UNIQUE */ - 281, /* (237) uniqueflag ::= */ - 221, /* (238) eidlist_opt ::= */ - 221, /* (239) eidlist_opt ::= LP eidlist RP */ - 232, /* (240) eidlist ::= eidlist COMMA nm collate sortorder */ - 232, /* (241) eidlist ::= nm collate sortorder */ - 282, /* (242) collate ::= */ - 282, /* (243) collate ::= COLLATE ID|STRING */ - 190, /* (244) cmd ::= DROP INDEX ifexists fullname */ - 190, /* (245) cmd ::= VACUUM vinto */ - 190, /* (246) cmd ::= VACUUM nm vinto */ - 283, /* (247) vinto ::= INTO expr */ - 283, /* (248) vinto ::= */ - 190, /* (249) cmd ::= PRAGMA nm dbnm */ - 190, /* (250) cmd ::= PRAGMA nm dbnm EQ nmnum */ - 190, /* (251) cmd ::= PRAGMA nm dbnm LP nmnum RP */ - 190, /* (252) cmd ::= PRAGMA nm dbnm EQ minus_num */ - 190, /* (253) cmd ::= PRAGMA nm dbnm LP minus_num RP */ - 211, /* (254) plus_num ::= PLUS INTEGER|FLOAT */ - 212, /* (255) minus_num ::= MINUS INTEGER|FLOAT */ - 190, /* (256) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ - 285, /* (257) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ - 287, /* (258) trigger_time ::= BEFORE|AFTER */ - 287, /* (259) trigger_time ::= INSTEAD OF */ - 287, /* (260) trigger_time ::= */ - 288, /* (261) trigger_event ::= DELETE|INSERT */ - 288, /* (262) trigger_event ::= UPDATE */ - 288, /* (263) trigger_event ::= UPDATE OF idlist */ - 290, /* (264) when_clause ::= */ - 290, /* (265) when_clause ::= WHEN expr */ - 286, /* (266) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ - 286, /* (267) trigger_cmd_list ::= trigger_cmd SEMI */ - 292, /* (268) trnm ::= nm DOT nm */ - 293, /* (269) tridxby ::= INDEXED BY nm */ - 293, /* (270) tridxby ::= NOT INDEXED */ - 291, /* (271) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */ - 291, /* (272) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */ - 291, /* (273) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ - 291, /* (274) trigger_cmd ::= scanpt select scanpt */ - 217, /* (275) expr ::= RAISE LP IGNORE RP */ - 217, /* (276) expr ::= RAISE LP raisetype COMMA nm RP */ - 236, /* (277) raisetype ::= ROLLBACK */ - 236, /* (278) raisetype ::= ABORT */ - 236, /* (279) raisetype ::= FAIL */ - 190, /* (280) cmd ::= DROP TRIGGER ifexists fullname */ - 190, /* (281) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ - 190, /* (282) cmd ::= DETACH database_kw_opt expr */ - 295, /* (283) key_opt ::= */ - 295, /* (284) key_opt ::= KEY expr */ - 190, /* (285) cmd ::= REINDEX */ - 190, /* (286) cmd ::= REINDEX nm dbnm */ - 190, /* (287) cmd ::= ANALYZE */ - 190, /* (288) cmd ::= ANALYZE nm dbnm */ - 190, /* (289) cmd ::= ALTER TABLE fullname RENAME TO nm */ - 190, /* (290) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ - 190, /* (291) cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */ - 296, /* (292) add_column_fullname ::= fullname */ - 190, /* (293) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ - 190, /* (294) cmd ::= create_vtab */ - 190, /* (295) cmd ::= create_vtab LP vtabarglist RP */ - 298, /* (296) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ - 300, /* (297) vtabarg ::= */ - 301, /* (298) vtabargtoken ::= ANY */ - 301, /* (299) vtabargtoken ::= lp anylist RP */ - 302, /* (300) lp ::= LP */ - 266, /* (301) with ::= WITH wqlist */ - 266, /* (302) with ::= WITH RECURSIVE wqlist */ - 305, /* (303) wqas ::= AS */ - 305, /* (304) wqas ::= AS MATERIALIZED */ - 305, /* (305) wqas ::= AS NOT MATERIALIZED */ - 304, /* (306) wqitem ::= nm eidlist_opt wqas LP select RP */ - 241, /* (307) wqlist ::= wqitem */ - 241, /* (308) wqlist ::= wqlist COMMA wqitem */ - 306, /* (309) windowdefn_list ::= windowdefn_list COMMA windowdefn */ - 307, /* (310) windowdefn ::= nm AS LP window RP */ - 308, /* (311) window ::= PARTITION BY nexprlist orderby_opt frame_opt */ - 308, /* (312) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ - 308, /* (313) window ::= ORDER BY sortlist frame_opt */ - 308, /* (314) window ::= nm ORDER BY sortlist frame_opt */ - 308, /* (315) window ::= nm frame_opt */ - 309, /* (316) frame_opt ::= */ - 309, /* (317) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ - 309, /* (318) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ - 313, /* (319) range_or_rows ::= RANGE|ROWS|GROUPS */ - 315, /* (320) frame_bound_s ::= frame_bound */ - 315, /* (321) frame_bound_s ::= UNBOUNDED PRECEDING */ - 316, /* (322) frame_bound_e ::= frame_bound */ - 316, /* (323) frame_bound_e ::= UNBOUNDED FOLLOWING */ - 314, /* (324) frame_bound ::= expr PRECEDING|FOLLOWING */ - 314, /* (325) frame_bound ::= CURRENT ROW */ - 317, /* (326) frame_exclude_opt ::= */ - 317, /* (327) frame_exclude_opt ::= EXCLUDE frame_exclude */ - 318, /* (328) frame_exclude ::= NO OTHERS */ - 318, /* (329) frame_exclude ::= CURRENT ROW */ - 318, /* (330) frame_exclude ::= GROUP|TIES */ - 251, /* (331) window_clause ::= WINDOW windowdefn_list */ - 273, /* (332) filter_over ::= filter_clause over_clause */ - 273, /* (333) filter_over ::= over_clause */ - 273, /* (334) filter_over ::= filter_clause */ - 312, /* (335) over_clause ::= OVER LP window RP */ - 312, /* (336) over_clause ::= OVER nm */ - 311, /* (337) filter_clause ::= FILTER LP WHERE expr RP */ - 185, /* (338) input ::= cmdlist */ - 186, /* (339) cmdlist ::= cmdlist ecmd */ - 186, /* (340) cmdlist ::= ecmd */ - 187, /* (341) ecmd ::= SEMI */ - 187, /* (342) ecmd ::= cmdx SEMI */ - 187, /* (343) ecmd ::= explain cmdx SEMI */ - 192, /* (344) trans_opt ::= */ - 192, /* (345) trans_opt ::= TRANSACTION */ - 192, /* (346) trans_opt ::= TRANSACTION nm */ - 194, /* (347) savepoint_opt ::= SAVEPOINT */ - 194, /* (348) savepoint_opt ::= */ - 190, /* (349) cmd ::= create_table create_table_args */ - 203, /* (350) table_option_set ::= table_option */ - 201, /* (351) columnlist ::= columnlist COMMA columnname carglist */ - 201, /* (352) columnlist ::= columnname carglist */ - 193, /* (353) nm ::= ID|INDEXED|JOIN_KW */ - 193, /* (354) nm ::= STRING */ - 208, /* (355) typetoken ::= typename */ - 209, /* (356) typename ::= ID|STRING */ - 210, /* (357) signed ::= plus_num */ - 210, /* (358) signed ::= minus_num */ - 207, /* (359) carglist ::= carglist ccons */ - 207, /* (360) carglist ::= */ - 215, /* (361) ccons ::= NULL onconf */ - 215, /* (362) ccons ::= GENERATED ALWAYS AS generated */ - 215, /* (363) ccons ::= AS generated */ - 202, /* (364) conslist_opt ::= COMMA conslist */ - 228, /* (365) conslist ::= conslist tconscomma tcons */ - 228, /* (366) conslist ::= tcons */ - 229, /* (367) tconscomma ::= */ - 233, /* (368) defer_subclause_opt ::= defer_subclause */ - 235, /* (369) resolvetype ::= raisetype */ - 239, /* (370) selectnowith ::= oneselect */ - 240, /* (371) oneselect ::= values */ - 254, /* (372) sclp ::= selcollist COMMA */ - 255, /* (373) as ::= ID|STRING */ - 264, /* (374) indexed_opt ::= indexed_by */ - 272, /* (375) returning ::= */ - 217, /* (376) expr ::= term */ - 274, /* (377) likeop ::= LIKE_KW|MATCH */ - 278, /* (378) case_operand ::= expr */ - 261, /* (379) exprlist ::= nexprlist */ - 284, /* (380) nmnum ::= plus_num */ - 284, /* (381) nmnum ::= nm */ - 284, /* (382) nmnum ::= ON */ - 284, /* (383) nmnum ::= DELETE */ - 284, /* (384) nmnum ::= DEFAULT */ - 211, /* (385) plus_num ::= INTEGER|FLOAT */ - 289, /* (386) foreach_clause ::= */ - 289, /* (387) foreach_clause ::= FOR EACH ROW */ - 292, /* (388) trnm ::= nm */ - 293, /* (389) tridxby ::= */ - 294, /* (390) database_kw_opt ::= DATABASE */ - 294, /* (391) database_kw_opt ::= */ - 297, /* (392) kwcolumn_opt ::= */ - 297, /* (393) kwcolumn_opt ::= COLUMNKW */ - 299, /* (394) vtabarglist ::= vtabarg */ - 299, /* (395) vtabarglist ::= vtabarglist COMMA vtabarg */ - 300, /* (396) vtabarg ::= vtabarg vtabargtoken */ - 303, /* (397) anylist ::= */ - 303, /* (398) anylist ::= anylist LP anylist RP */ - 303, /* (399) anylist ::= anylist ANY */ - 266, /* (400) with ::= */ - 306, /* (401) windowdefn_list ::= windowdefn */ - 308, /* (402) window ::= frame_opt */ + 217, /* (188) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP */ + 217, /* (189) expr ::= ID|INDEXED|JOIN_KW LP STAR RP */ + 217, /* (190) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP filter_over */ + 217, /* (191) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP filter_over */ + 217, /* (192) expr ::= ID|INDEXED|JOIN_KW LP STAR RP filter_over */ + 216, /* (193) term ::= CTIME_KW */ + 217, /* (194) expr ::= LP nexprlist COMMA expr RP */ + 217, /* (195) expr ::= expr AND expr */ + 217, /* (196) expr ::= expr OR expr */ + 217, /* (197) expr ::= expr LT|GT|GE|LE expr */ + 217, /* (198) expr ::= expr EQ|NE expr */ + 217, /* (199) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ + 217, /* (200) expr ::= expr PLUS|MINUS expr */ + 217, /* (201) expr ::= expr STAR|SLASH|REM expr */ + 217, /* (202) expr ::= expr CONCAT expr */ + 274, /* (203) likeop ::= NOT LIKE_KW|MATCH */ + 217, /* (204) expr ::= expr likeop expr */ + 217, /* (205) expr ::= expr likeop expr ESCAPE expr */ + 217, /* (206) expr ::= expr ISNULL|NOTNULL */ + 217, /* (207) expr ::= expr NOT NULL */ + 217, /* (208) expr ::= expr IS expr */ + 217, /* (209) expr ::= expr IS NOT expr */ + 217, /* (210) expr ::= expr IS NOT DISTINCT FROM expr */ + 217, /* (211) expr ::= expr IS DISTINCT FROM expr */ + 217, /* (212) expr ::= NOT expr */ + 217, /* (213) expr ::= BITNOT expr */ + 217, /* (214) expr ::= PLUS|MINUS expr */ + 217, /* (215) expr ::= expr PTR expr */ + 275, /* (216) between_op ::= BETWEEN */ + 275, /* (217) between_op ::= NOT BETWEEN */ + 217, /* (218) expr ::= expr between_op expr AND expr */ + 276, /* (219) in_op ::= IN */ + 276, /* (220) in_op ::= NOT IN */ + 217, /* (221) expr ::= expr in_op LP exprlist RP */ + 217, /* (222) expr ::= LP select RP */ + 217, /* (223) expr ::= expr in_op LP select RP */ + 217, /* (224) expr ::= expr in_op nm dbnm paren_exprlist */ + 217, /* (225) expr ::= EXISTS LP select RP */ + 217, /* (226) expr ::= CASE case_operand case_exprlist case_else END */ + 279, /* (227) case_exprlist ::= case_exprlist WHEN expr THEN expr */ + 279, /* (228) case_exprlist ::= WHEN expr THEN expr */ + 280, /* (229) case_else ::= ELSE expr */ + 280, /* (230) case_else ::= */ + 278, /* (231) case_operand ::= */ + 261, /* (232) exprlist ::= */ + 253, /* (233) nexprlist ::= nexprlist COMMA expr */ + 253, /* (234) nexprlist ::= expr */ + 277, /* (235) paren_exprlist ::= */ + 277, /* (236) paren_exprlist ::= LP exprlist RP */ + 190, /* (237) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ + 281, /* (238) uniqueflag ::= UNIQUE */ + 281, /* (239) uniqueflag ::= */ + 221, /* (240) eidlist_opt ::= */ + 221, /* (241) eidlist_opt ::= LP eidlist RP */ + 232, /* (242) eidlist ::= eidlist COMMA nm collate sortorder */ + 232, /* (243) eidlist ::= nm collate sortorder */ + 282, /* (244) collate ::= */ + 282, /* (245) collate ::= COLLATE ID|STRING */ + 190, /* (246) cmd ::= DROP INDEX ifexists fullname */ + 190, /* (247) cmd ::= VACUUM vinto */ + 190, /* (248) cmd ::= VACUUM nm vinto */ + 283, /* (249) vinto ::= INTO expr */ + 283, /* (250) vinto ::= */ + 190, /* (251) cmd ::= PRAGMA nm dbnm */ + 190, /* (252) cmd ::= PRAGMA nm dbnm EQ nmnum */ + 190, /* (253) cmd ::= PRAGMA nm dbnm LP nmnum RP */ + 190, /* (254) cmd ::= PRAGMA nm dbnm EQ minus_num */ + 190, /* (255) cmd ::= PRAGMA nm dbnm LP minus_num RP */ + 211, /* (256) plus_num ::= PLUS INTEGER|FLOAT */ + 212, /* (257) minus_num ::= MINUS INTEGER|FLOAT */ + 190, /* (258) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ + 285, /* (259) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ + 287, /* (260) trigger_time ::= BEFORE|AFTER */ + 287, /* (261) trigger_time ::= INSTEAD OF */ + 287, /* (262) trigger_time ::= */ + 288, /* (263) trigger_event ::= DELETE|INSERT */ + 288, /* (264) trigger_event ::= UPDATE */ + 288, /* (265) trigger_event ::= UPDATE OF idlist */ + 290, /* (266) when_clause ::= */ + 290, /* (267) when_clause ::= WHEN expr */ + 286, /* (268) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ + 286, /* (269) trigger_cmd_list ::= trigger_cmd SEMI */ + 292, /* (270) trnm ::= nm DOT nm */ + 293, /* (271) tridxby ::= INDEXED BY nm */ + 293, /* (272) tridxby ::= NOT INDEXED */ + 291, /* (273) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */ + 291, /* (274) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */ + 291, /* (275) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ + 291, /* (276) trigger_cmd ::= scanpt select scanpt */ + 217, /* (277) expr ::= RAISE LP IGNORE RP */ + 217, /* (278) expr ::= RAISE LP raisetype COMMA nm RP */ + 236, /* (279) raisetype ::= ROLLBACK */ + 236, /* (280) raisetype ::= ABORT */ + 236, /* (281) raisetype ::= FAIL */ + 190, /* (282) cmd ::= DROP TRIGGER ifexists fullname */ + 190, /* (283) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ + 190, /* (284) cmd ::= DETACH database_kw_opt expr */ + 295, /* (285) key_opt ::= */ + 295, /* (286) key_opt ::= KEY expr */ + 190, /* (287) cmd ::= REINDEX */ + 190, /* (288) cmd ::= REINDEX nm dbnm */ + 190, /* (289) cmd ::= ANALYZE */ + 190, /* (290) cmd ::= ANALYZE nm dbnm */ + 190, /* (291) cmd ::= ALTER TABLE fullname RENAME TO nm */ + 190, /* (292) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ + 190, /* (293) cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */ + 296, /* (294) add_column_fullname ::= fullname */ + 190, /* (295) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ + 190, /* (296) cmd ::= create_vtab */ + 190, /* (297) cmd ::= create_vtab LP vtabarglist RP */ + 298, /* (298) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ + 300, /* (299) vtabarg ::= */ + 301, /* (300) vtabargtoken ::= ANY */ + 301, /* (301) vtabargtoken ::= lp anylist RP */ + 302, /* (302) lp ::= LP */ + 266, /* (303) with ::= WITH wqlist */ + 266, /* (304) with ::= WITH RECURSIVE wqlist */ + 305, /* (305) wqas ::= AS */ + 305, /* (306) wqas ::= AS MATERIALIZED */ + 305, /* (307) wqas ::= AS NOT MATERIALIZED */ + 304, /* (308) wqitem ::= nm eidlist_opt wqas LP select RP */ + 241, /* (309) wqlist ::= wqitem */ + 241, /* (310) wqlist ::= wqlist COMMA wqitem */ + 306, /* (311) windowdefn_list ::= windowdefn_list COMMA windowdefn */ + 307, /* (312) windowdefn ::= nm AS LP window RP */ + 308, /* (313) window ::= PARTITION BY nexprlist orderby_opt frame_opt */ + 308, /* (314) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ + 308, /* (315) window ::= ORDER BY sortlist frame_opt */ + 308, /* (316) window ::= nm ORDER BY sortlist frame_opt */ + 308, /* (317) window ::= nm frame_opt */ + 309, /* (318) frame_opt ::= */ + 309, /* (319) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ + 309, /* (320) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ + 313, /* (321) range_or_rows ::= RANGE|ROWS|GROUPS */ + 315, /* (322) frame_bound_s ::= frame_bound */ + 315, /* (323) frame_bound_s ::= UNBOUNDED PRECEDING */ + 316, /* (324) frame_bound_e ::= frame_bound */ + 316, /* (325) frame_bound_e ::= UNBOUNDED FOLLOWING */ + 314, /* (326) frame_bound ::= expr PRECEDING|FOLLOWING */ + 314, /* (327) frame_bound ::= CURRENT ROW */ + 317, /* (328) frame_exclude_opt ::= */ + 317, /* (329) frame_exclude_opt ::= EXCLUDE frame_exclude */ + 318, /* (330) frame_exclude ::= NO OTHERS */ + 318, /* (331) frame_exclude ::= CURRENT ROW */ + 318, /* (332) frame_exclude ::= GROUP|TIES */ + 251, /* (333) window_clause ::= WINDOW windowdefn_list */ + 273, /* (334) filter_over ::= filter_clause over_clause */ + 273, /* (335) filter_over ::= over_clause */ + 273, /* (336) filter_over ::= filter_clause */ + 312, /* (337) over_clause ::= OVER LP window RP */ + 312, /* (338) over_clause ::= OVER nm */ + 311, /* (339) filter_clause ::= FILTER LP WHERE expr RP */ + 185, /* (340) input ::= cmdlist */ + 186, /* (341) cmdlist ::= cmdlist ecmd */ + 186, /* (342) cmdlist ::= ecmd */ + 187, /* (343) ecmd ::= SEMI */ + 187, /* (344) ecmd ::= cmdx SEMI */ + 187, /* (345) ecmd ::= explain cmdx SEMI */ + 192, /* (346) trans_opt ::= */ + 192, /* (347) trans_opt ::= TRANSACTION */ + 192, /* (348) trans_opt ::= TRANSACTION nm */ + 194, /* (349) savepoint_opt ::= SAVEPOINT */ + 194, /* (350) savepoint_opt ::= */ + 190, /* (351) cmd ::= create_table create_table_args */ + 203, /* (352) table_option_set ::= table_option */ + 201, /* (353) columnlist ::= columnlist COMMA columnname carglist */ + 201, /* (354) columnlist ::= columnname carglist */ + 193, /* (355) nm ::= ID|INDEXED|JOIN_KW */ + 193, /* (356) nm ::= STRING */ + 208, /* (357) typetoken ::= typename */ + 209, /* (358) typename ::= ID|STRING */ + 210, /* (359) signed ::= plus_num */ + 210, /* (360) signed ::= minus_num */ + 207, /* (361) carglist ::= carglist ccons */ + 207, /* (362) carglist ::= */ + 215, /* (363) ccons ::= NULL onconf */ + 215, /* (364) ccons ::= GENERATED ALWAYS AS generated */ + 215, /* (365) ccons ::= AS generated */ + 202, /* (366) conslist_opt ::= COMMA conslist */ + 228, /* (367) conslist ::= conslist tconscomma tcons */ + 228, /* (368) conslist ::= tcons */ + 229, /* (369) tconscomma ::= */ + 233, /* (370) defer_subclause_opt ::= defer_subclause */ + 235, /* (371) resolvetype ::= raisetype */ + 239, /* (372) selectnowith ::= oneselect */ + 240, /* (373) oneselect ::= values */ + 254, /* (374) sclp ::= selcollist COMMA */ + 255, /* (375) as ::= ID|STRING */ + 264, /* (376) indexed_opt ::= indexed_by */ + 272, /* (377) returning ::= */ + 217, /* (378) expr ::= term */ + 274, /* (379) likeop ::= LIKE_KW|MATCH */ + 278, /* (380) case_operand ::= expr */ + 261, /* (381) exprlist ::= nexprlist */ + 284, /* (382) nmnum ::= plus_num */ + 284, /* (383) nmnum ::= nm */ + 284, /* (384) nmnum ::= ON */ + 284, /* (385) nmnum ::= DELETE */ + 284, /* (386) nmnum ::= DEFAULT */ + 211, /* (387) plus_num ::= INTEGER|FLOAT */ + 289, /* (388) foreach_clause ::= */ + 289, /* (389) foreach_clause ::= FOR EACH ROW */ + 292, /* (390) trnm ::= nm */ + 293, /* (391) tridxby ::= */ + 294, /* (392) database_kw_opt ::= DATABASE */ + 294, /* (393) database_kw_opt ::= */ + 297, /* (394) kwcolumn_opt ::= */ + 297, /* (395) kwcolumn_opt ::= COLUMNKW */ + 299, /* (396) vtabarglist ::= vtabarg */ + 299, /* (397) vtabarglist ::= vtabarglist COMMA vtabarg */ + 300, /* (398) vtabarg ::= vtabarg vtabargtoken */ + 303, /* (399) anylist ::= */ + 303, /* (400) anylist ::= anylist LP anylist RP */ + 303, /* (401) anylist ::= anylist ANY */ + 266, /* (402) with ::= */ + 306, /* (403) windowdefn_list ::= windowdefn */ + 308, /* (404) window ::= frame_opt */ }; /* For rule J, yyRuleInfoNRhs[J] contains the negative of the number @@ -172546,221 +174236,223 @@ static const signed char yyRuleInfoNRhs[] = { -3, /* (185) expr ::= expr COLLATE ID|STRING */ -6, /* (186) expr ::= CAST LP expr AS typetoken RP */ -5, /* (187) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP */ - -4, /* (188) expr ::= ID|INDEXED|JOIN_KW LP STAR RP */ - -6, /* (189) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP filter_over */ - -5, /* (190) expr ::= ID|INDEXED|JOIN_KW LP STAR RP filter_over */ - -1, /* (191) term ::= CTIME_KW */ - -5, /* (192) expr ::= LP nexprlist COMMA expr RP */ - -3, /* (193) expr ::= expr AND expr */ - -3, /* (194) expr ::= expr OR expr */ - -3, /* (195) expr ::= expr LT|GT|GE|LE expr */ - -3, /* (196) expr ::= expr EQ|NE expr */ - -3, /* (197) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ - -3, /* (198) expr ::= expr PLUS|MINUS expr */ - -3, /* (199) expr ::= expr STAR|SLASH|REM expr */ - -3, /* (200) expr ::= expr CONCAT expr */ - -2, /* (201) likeop ::= NOT LIKE_KW|MATCH */ - -3, /* (202) expr ::= expr likeop expr */ - -5, /* (203) expr ::= expr likeop expr ESCAPE expr */ - -2, /* (204) expr ::= expr ISNULL|NOTNULL */ - -3, /* (205) expr ::= expr NOT NULL */ - -3, /* (206) expr ::= expr IS expr */ - -4, /* (207) expr ::= expr IS NOT expr */ - -6, /* (208) expr ::= expr IS NOT DISTINCT FROM expr */ - -5, /* (209) expr ::= expr IS DISTINCT FROM expr */ - -2, /* (210) expr ::= NOT expr */ - -2, /* (211) expr ::= BITNOT expr */ - -2, /* (212) expr ::= PLUS|MINUS expr */ - -3, /* (213) expr ::= expr PTR expr */ - -1, /* (214) between_op ::= BETWEEN */ - -2, /* (215) between_op ::= NOT BETWEEN */ - -5, /* (216) expr ::= expr between_op expr AND expr */ - -1, /* (217) in_op ::= IN */ - -2, /* (218) in_op ::= NOT IN */ - -5, /* (219) expr ::= expr in_op LP exprlist RP */ - -3, /* (220) expr ::= LP select RP */ - -5, /* (221) expr ::= expr in_op LP select RP */ - -5, /* (222) expr ::= expr in_op nm dbnm paren_exprlist */ - -4, /* (223) expr ::= EXISTS LP select RP */ - -5, /* (224) expr ::= CASE case_operand case_exprlist case_else END */ - -5, /* (225) case_exprlist ::= case_exprlist WHEN expr THEN expr */ - -4, /* (226) case_exprlist ::= WHEN expr THEN expr */ - -2, /* (227) case_else ::= ELSE expr */ - 0, /* (228) case_else ::= */ - 0, /* (229) case_operand ::= */ - 0, /* (230) exprlist ::= */ - -3, /* (231) nexprlist ::= nexprlist COMMA expr */ - -1, /* (232) nexprlist ::= expr */ - 0, /* (233) paren_exprlist ::= */ - -3, /* (234) paren_exprlist ::= LP exprlist RP */ - -12, /* (235) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ - -1, /* (236) uniqueflag ::= UNIQUE */ - 0, /* (237) uniqueflag ::= */ - 0, /* (238) eidlist_opt ::= */ - -3, /* (239) eidlist_opt ::= LP eidlist RP */ - -5, /* (240) eidlist ::= eidlist COMMA nm collate sortorder */ - -3, /* (241) eidlist ::= nm collate sortorder */ - 0, /* (242) collate ::= */ - -2, /* (243) collate ::= COLLATE ID|STRING */ - -4, /* (244) cmd ::= DROP INDEX ifexists fullname */ - -2, /* (245) cmd ::= VACUUM vinto */ - -3, /* (246) cmd ::= VACUUM nm vinto */ - -2, /* (247) vinto ::= INTO expr */ - 0, /* (248) vinto ::= */ - -3, /* (249) cmd ::= PRAGMA nm dbnm */ - -5, /* (250) cmd ::= PRAGMA nm dbnm EQ nmnum */ - -6, /* (251) cmd ::= PRAGMA nm dbnm LP nmnum RP */ - -5, /* (252) cmd ::= PRAGMA nm dbnm EQ minus_num */ - -6, /* (253) cmd ::= PRAGMA nm dbnm LP minus_num RP */ - -2, /* (254) plus_num ::= PLUS INTEGER|FLOAT */ - -2, /* (255) minus_num ::= MINUS INTEGER|FLOAT */ - -5, /* (256) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ - -11, /* (257) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ - -1, /* (258) trigger_time ::= BEFORE|AFTER */ - -2, /* (259) trigger_time ::= INSTEAD OF */ - 0, /* (260) trigger_time ::= */ - -1, /* (261) trigger_event ::= DELETE|INSERT */ - -1, /* (262) trigger_event ::= UPDATE */ - -3, /* (263) trigger_event ::= UPDATE OF idlist */ - 0, /* (264) when_clause ::= */ - -2, /* (265) when_clause ::= WHEN expr */ - -3, /* (266) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ - -2, /* (267) trigger_cmd_list ::= trigger_cmd SEMI */ - -3, /* (268) trnm ::= nm DOT nm */ - -3, /* (269) tridxby ::= INDEXED BY nm */ - -2, /* (270) tridxby ::= NOT INDEXED */ - -9, /* (271) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */ - -8, /* (272) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */ - -6, /* (273) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ - -3, /* (274) trigger_cmd ::= scanpt select scanpt */ - -4, /* (275) expr ::= RAISE LP IGNORE RP */ - -6, /* (276) expr ::= RAISE LP raisetype COMMA nm RP */ - -1, /* (277) raisetype ::= ROLLBACK */ - -1, /* (278) raisetype ::= ABORT */ - -1, /* (279) raisetype ::= FAIL */ - -4, /* (280) cmd ::= DROP TRIGGER ifexists fullname */ - -6, /* (281) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ - -3, /* (282) cmd ::= DETACH database_kw_opt expr */ - 0, /* (283) key_opt ::= */ - -2, /* (284) key_opt ::= KEY expr */ - -1, /* (285) cmd ::= REINDEX */ - -3, /* (286) cmd ::= REINDEX nm dbnm */ - -1, /* (287) cmd ::= ANALYZE */ - -3, /* (288) cmd ::= ANALYZE nm dbnm */ - -6, /* (289) cmd ::= ALTER TABLE fullname RENAME TO nm */ - -7, /* (290) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ - -6, /* (291) cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */ - -1, /* (292) add_column_fullname ::= fullname */ - -8, /* (293) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ - -1, /* (294) cmd ::= create_vtab */ - -4, /* (295) cmd ::= create_vtab LP vtabarglist RP */ - -8, /* (296) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ - 0, /* (297) vtabarg ::= */ - -1, /* (298) vtabargtoken ::= ANY */ - -3, /* (299) vtabargtoken ::= lp anylist RP */ - -1, /* (300) lp ::= LP */ - -2, /* (301) with ::= WITH wqlist */ - -3, /* (302) with ::= WITH RECURSIVE wqlist */ - -1, /* (303) wqas ::= AS */ - -2, /* (304) wqas ::= AS MATERIALIZED */ - -3, /* (305) wqas ::= AS NOT MATERIALIZED */ - -6, /* (306) wqitem ::= nm eidlist_opt wqas LP select RP */ - -1, /* (307) wqlist ::= wqitem */ - -3, /* (308) wqlist ::= wqlist COMMA wqitem */ - -3, /* (309) windowdefn_list ::= windowdefn_list COMMA windowdefn */ - -5, /* (310) windowdefn ::= nm AS LP window RP */ - -5, /* (311) window ::= PARTITION BY nexprlist orderby_opt frame_opt */ - -6, /* (312) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ - -4, /* (313) window ::= ORDER BY sortlist frame_opt */ - -5, /* (314) window ::= nm ORDER BY sortlist frame_opt */ - -2, /* (315) window ::= nm frame_opt */ - 0, /* (316) frame_opt ::= */ - -3, /* (317) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ - -6, /* (318) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ - -1, /* (319) range_or_rows ::= RANGE|ROWS|GROUPS */ - -1, /* (320) frame_bound_s ::= frame_bound */ - -2, /* (321) frame_bound_s ::= UNBOUNDED PRECEDING */ - -1, /* (322) frame_bound_e ::= frame_bound */ - -2, /* (323) frame_bound_e ::= UNBOUNDED FOLLOWING */ - -2, /* (324) frame_bound ::= expr PRECEDING|FOLLOWING */ - -2, /* (325) frame_bound ::= CURRENT ROW */ - 0, /* (326) frame_exclude_opt ::= */ - -2, /* (327) frame_exclude_opt ::= EXCLUDE frame_exclude */ - -2, /* (328) frame_exclude ::= NO OTHERS */ - -2, /* (329) frame_exclude ::= CURRENT ROW */ - -1, /* (330) frame_exclude ::= GROUP|TIES */ - -2, /* (331) window_clause ::= WINDOW windowdefn_list */ - -2, /* (332) filter_over ::= filter_clause over_clause */ - -1, /* (333) filter_over ::= over_clause */ - -1, /* (334) filter_over ::= filter_clause */ - -4, /* (335) over_clause ::= OVER LP window RP */ - -2, /* (336) over_clause ::= OVER nm */ - -5, /* (337) filter_clause ::= FILTER LP WHERE expr RP */ - -1, /* (338) input ::= cmdlist */ - -2, /* (339) cmdlist ::= cmdlist ecmd */ - -1, /* (340) cmdlist ::= ecmd */ - -1, /* (341) ecmd ::= SEMI */ - -2, /* (342) ecmd ::= cmdx SEMI */ - -3, /* (343) ecmd ::= explain cmdx SEMI */ - 0, /* (344) trans_opt ::= */ - -1, /* (345) trans_opt ::= TRANSACTION */ - -2, /* (346) trans_opt ::= TRANSACTION nm */ - -1, /* (347) savepoint_opt ::= SAVEPOINT */ - 0, /* (348) savepoint_opt ::= */ - -2, /* (349) cmd ::= create_table create_table_args */ - -1, /* (350) table_option_set ::= table_option */ - -4, /* (351) columnlist ::= columnlist COMMA columnname carglist */ - -2, /* (352) columnlist ::= columnname carglist */ - -1, /* (353) nm ::= ID|INDEXED|JOIN_KW */ - -1, /* (354) nm ::= STRING */ - -1, /* (355) typetoken ::= typename */ - -1, /* (356) typename ::= ID|STRING */ - -1, /* (357) signed ::= plus_num */ - -1, /* (358) signed ::= minus_num */ - -2, /* (359) carglist ::= carglist ccons */ - 0, /* (360) carglist ::= */ - -2, /* (361) ccons ::= NULL onconf */ - -4, /* (362) ccons ::= GENERATED ALWAYS AS generated */ - -2, /* (363) ccons ::= AS generated */ - -2, /* (364) conslist_opt ::= COMMA conslist */ - -3, /* (365) conslist ::= conslist tconscomma tcons */ - -1, /* (366) conslist ::= tcons */ - 0, /* (367) tconscomma ::= */ - -1, /* (368) defer_subclause_opt ::= defer_subclause */ - -1, /* (369) resolvetype ::= raisetype */ - -1, /* (370) selectnowith ::= oneselect */ - -1, /* (371) oneselect ::= values */ - -2, /* (372) sclp ::= selcollist COMMA */ - -1, /* (373) as ::= ID|STRING */ - -1, /* (374) indexed_opt ::= indexed_by */ - 0, /* (375) returning ::= */ - -1, /* (376) expr ::= term */ - -1, /* (377) likeop ::= LIKE_KW|MATCH */ - -1, /* (378) case_operand ::= expr */ - -1, /* (379) exprlist ::= nexprlist */ - -1, /* (380) nmnum ::= plus_num */ - -1, /* (381) nmnum ::= nm */ - -1, /* (382) nmnum ::= ON */ - -1, /* (383) nmnum ::= DELETE */ - -1, /* (384) nmnum ::= DEFAULT */ - -1, /* (385) plus_num ::= INTEGER|FLOAT */ - 0, /* (386) foreach_clause ::= */ - -3, /* (387) foreach_clause ::= FOR EACH ROW */ - -1, /* (388) trnm ::= nm */ - 0, /* (389) tridxby ::= */ - -1, /* (390) database_kw_opt ::= DATABASE */ - 0, /* (391) database_kw_opt ::= */ - 0, /* (392) kwcolumn_opt ::= */ - -1, /* (393) kwcolumn_opt ::= COLUMNKW */ - -1, /* (394) vtabarglist ::= vtabarg */ - -3, /* (395) vtabarglist ::= vtabarglist COMMA vtabarg */ - -2, /* (396) vtabarg ::= vtabarg vtabargtoken */ - 0, /* (397) anylist ::= */ - -4, /* (398) anylist ::= anylist LP anylist RP */ - -2, /* (399) anylist ::= anylist ANY */ - 0, /* (400) with ::= */ - -1, /* (401) windowdefn_list ::= windowdefn */ - -1, /* (402) window ::= frame_opt */ + -8, /* (188) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP */ + -4, /* (189) expr ::= ID|INDEXED|JOIN_KW LP STAR RP */ + -6, /* (190) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP filter_over */ + -9, /* (191) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP filter_over */ + -5, /* (192) expr ::= ID|INDEXED|JOIN_KW LP STAR RP filter_over */ + -1, /* (193) term ::= CTIME_KW */ + -5, /* (194) expr ::= LP nexprlist COMMA expr RP */ + -3, /* (195) expr ::= expr AND expr */ + -3, /* (196) expr ::= expr OR expr */ + -3, /* (197) expr ::= expr LT|GT|GE|LE expr */ + -3, /* (198) expr ::= expr EQ|NE expr */ + -3, /* (199) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ + -3, /* (200) expr ::= expr PLUS|MINUS expr */ + -3, /* (201) expr ::= expr STAR|SLASH|REM expr */ + -3, /* (202) expr ::= expr CONCAT expr */ + -2, /* (203) likeop ::= NOT LIKE_KW|MATCH */ + -3, /* (204) expr ::= expr likeop expr */ + -5, /* (205) expr ::= expr likeop expr ESCAPE expr */ + -2, /* (206) expr ::= expr ISNULL|NOTNULL */ + -3, /* (207) expr ::= expr NOT NULL */ + -3, /* (208) expr ::= expr IS expr */ + -4, /* (209) expr ::= expr IS NOT expr */ + -6, /* (210) expr ::= expr IS NOT DISTINCT FROM expr */ + -5, /* (211) expr ::= expr IS DISTINCT FROM expr */ + -2, /* (212) expr ::= NOT expr */ + -2, /* (213) expr ::= BITNOT expr */ + -2, /* (214) expr ::= PLUS|MINUS expr */ + -3, /* (215) expr ::= expr PTR expr */ + -1, /* (216) between_op ::= BETWEEN */ + -2, /* (217) between_op ::= NOT BETWEEN */ + -5, /* (218) expr ::= expr between_op expr AND expr */ + -1, /* (219) in_op ::= IN */ + -2, /* (220) in_op ::= NOT IN */ + -5, /* (221) expr ::= expr in_op LP exprlist RP */ + -3, /* (222) expr ::= LP select RP */ + -5, /* (223) expr ::= expr in_op LP select RP */ + -5, /* (224) expr ::= expr in_op nm dbnm paren_exprlist */ + -4, /* (225) expr ::= EXISTS LP select RP */ + -5, /* (226) expr ::= CASE case_operand case_exprlist case_else END */ + -5, /* (227) case_exprlist ::= case_exprlist WHEN expr THEN expr */ + -4, /* (228) case_exprlist ::= WHEN expr THEN expr */ + -2, /* (229) case_else ::= ELSE expr */ + 0, /* (230) case_else ::= */ + 0, /* (231) case_operand ::= */ + 0, /* (232) exprlist ::= */ + -3, /* (233) nexprlist ::= nexprlist COMMA expr */ + -1, /* (234) nexprlist ::= expr */ + 0, /* (235) paren_exprlist ::= */ + -3, /* (236) paren_exprlist ::= LP exprlist RP */ + -12, /* (237) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ + -1, /* (238) uniqueflag ::= UNIQUE */ + 0, /* (239) uniqueflag ::= */ + 0, /* (240) eidlist_opt ::= */ + -3, /* (241) eidlist_opt ::= LP eidlist RP */ + -5, /* (242) eidlist ::= eidlist COMMA nm collate sortorder */ + -3, /* (243) eidlist ::= nm collate sortorder */ + 0, /* (244) collate ::= */ + -2, /* (245) collate ::= COLLATE ID|STRING */ + -4, /* (246) cmd ::= DROP INDEX ifexists fullname */ + -2, /* (247) cmd ::= VACUUM vinto */ + -3, /* (248) cmd ::= VACUUM nm vinto */ + -2, /* (249) vinto ::= INTO expr */ + 0, /* (250) vinto ::= */ + -3, /* (251) cmd ::= PRAGMA nm dbnm */ + -5, /* (252) cmd ::= PRAGMA nm dbnm EQ nmnum */ + -6, /* (253) cmd ::= PRAGMA nm dbnm LP nmnum RP */ + -5, /* (254) cmd ::= PRAGMA nm dbnm EQ minus_num */ + -6, /* (255) cmd ::= PRAGMA nm dbnm LP minus_num RP */ + -2, /* (256) plus_num ::= PLUS INTEGER|FLOAT */ + -2, /* (257) minus_num ::= MINUS INTEGER|FLOAT */ + -5, /* (258) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ + -11, /* (259) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ + -1, /* (260) trigger_time ::= BEFORE|AFTER */ + -2, /* (261) trigger_time ::= INSTEAD OF */ + 0, /* (262) trigger_time ::= */ + -1, /* (263) trigger_event ::= DELETE|INSERT */ + -1, /* (264) trigger_event ::= UPDATE */ + -3, /* (265) trigger_event ::= UPDATE OF idlist */ + 0, /* (266) when_clause ::= */ + -2, /* (267) when_clause ::= WHEN expr */ + -3, /* (268) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ + -2, /* (269) trigger_cmd_list ::= trigger_cmd SEMI */ + -3, /* (270) trnm ::= nm DOT nm */ + -3, /* (271) tridxby ::= INDEXED BY nm */ + -2, /* (272) tridxby ::= NOT INDEXED */ + -9, /* (273) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */ + -8, /* (274) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */ + -6, /* (275) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ + -3, /* (276) trigger_cmd ::= scanpt select scanpt */ + -4, /* (277) expr ::= RAISE LP IGNORE RP */ + -6, /* (278) expr ::= RAISE LP raisetype COMMA nm RP */ + -1, /* (279) raisetype ::= ROLLBACK */ + -1, /* (280) raisetype ::= ABORT */ + -1, /* (281) raisetype ::= FAIL */ + -4, /* (282) cmd ::= DROP TRIGGER ifexists fullname */ + -6, /* (283) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ + -3, /* (284) cmd ::= DETACH database_kw_opt expr */ + 0, /* (285) key_opt ::= */ + -2, /* (286) key_opt ::= KEY expr */ + -1, /* (287) cmd ::= REINDEX */ + -3, /* (288) cmd ::= REINDEX nm dbnm */ + -1, /* (289) cmd ::= ANALYZE */ + -3, /* (290) cmd ::= ANALYZE nm dbnm */ + -6, /* (291) cmd ::= ALTER TABLE fullname RENAME TO nm */ + -7, /* (292) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ + -6, /* (293) cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */ + -1, /* (294) add_column_fullname ::= fullname */ + -8, /* (295) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ + -1, /* (296) cmd ::= create_vtab */ + -4, /* (297) cmd ::= create_vtab LP vtabarglist RP */ + -8, /* (298) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ + 0, /* (299) vtabarg ::= */ + -1, /* (300) vtabargtoken ::= ANY */ + -3, /* (301) vtabargtoken ::= lp anylist RP */ + -1, /* (302) lp ::= LP */ + -2, /* (303) with ::= WITH wqlist */ + -3, /* (304) with ::= WITH RECURSIVE wqlist */ + -1, /* (305) wqas ::= AS */ + -2, /* (306) wqas ::= AS MATERIALIZED */ + -3, /* (307) wqas ::= AS NOT MATERIALIZED */ + -6, /* (308) wqitem ::= nm eidlist_opt wqas LP select RP */ + -1, /* (309) wqlist ::= wqitem */ + -3, /* (310) wqlist ::= wqlist COMMA wqitem */ + -3, /* (311) windowdefn_list ::= windowdefn_list COMMA windowdefn */ + -5, /* (312) windowdefn ::= nm AS LP window RP */ + -5, /* (313) window ::= PARTITION BY nexprlist orderby_opt frame_opt */ + -6, /* (314) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ + -4, /* (315) window ::= ORDER BY sortlist frame_opt */ + -5, /* (316) window ::= nm ORDER BY sortlist frame_opt */ + -2, /* (317) window ::= nm frame_opt */ + 0, /* (318) frame_opt ::= */ + -3, /* (319) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ + -6, /* (320) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ + -1, /* (321) range_or_rows ::= RANGE|ROWS|GROUPS */ + -1, /* (322) frame_bound_s ::= frame_bound */ + -2, /* (323) frame_bound_s ::= UNBOUNDED PRECEDING */ + -1, /* (324) frame_bound_e ::= frame_bound */ + -2, /* (325) frame_bound_e ::= UNBOUNDED FOLLOWING */ + -2, /* (326) frame_bound ::= expr PRECEDING|FOLLOWING */ + -2, /* (327) frame_bound ::= CURRENT ROW */ + 0, /* (328) frame_exclude_opt ::= */ + -2, /* (329) frame_exclude_opt ::= EXCLUDE frame_exclude */ + -2, /* (330) frame_exclude ::= NO OTHERS */ + -2, /* (331) frame_exclude ::= CURRENT ROW */ + -1, /* (332) frame_exclude ::= GROUP|TIES */ + -2, /* (333) window_clause ::= WINDOW windowdefn_list */ + -2, /* (334) filter_over ::= filter_clause over_clause */ + -1, /* (335) filter_over ::= over_clause */ + -1, /* (336) filter_over ::= filter_clause */ + -4, /* (337) over_clause ::= OVER LP window RP */ + -2, /* (338) over_clause ::= OVER nm */ + -5, /* (339) filter_clause ::= FILTER LP WHERE expr RP */ + -1, /* (340) input ::= cmdlist */ + -2, /* (341) cmdlist ::= cmdlist ecmd */ + -1, /* (342) cmdlist ::= ecmd */ + -1, /* (343) ecmd ::= SEMI */ + -2, /* (344) ecmd ::= cmdx SEMI */ + -3, /* (345) ecmd ::= explain cmdx SEMI */ + 0, /* (346) trans_opt ::= */ + -1, /* (347) trans_opt ::= TRANSACTION */ + -2, /* (348) trans_opt ::= TRANSACTION nm */ + -1, /* (349) savepoint_opt ::= SAVEPOINT */ + 0, /* (350) savepoint_opt ::= */ + -2, /* (351) cmd ::= create_table create_table_args */ + -1, /* (352) table_option_set ::= table_option */ + -4, /* (353) columnlist ::= columnlist COMMA columnname carglist */ + -2, /* (354) columnlist ::= columnname carglist */ + -1, /* (355) nm ::= ID|INDEXED|JOIN_KW */ + -1, /* (356) nm ::= STRING */ + -1, /* (357) typetoken ::= typename */ + -1, /* (358) typename ::= ID|STRING */ + -1, /* (359) signed ::= plus_num */ + -1, /* (360) signed ::= minus_num */ + -2, /* (361) carglist ::= carglist ccons */ + 0, /* (362) carglist ::= */ + -2, /* (363) ccons ::= NULL onconf */ + -4, /* (364) ccons ::= GENERATED ALWAYS AS generated */ + -2, /* (365) ccons ::= AS generated */ + -2, /* (366) conslist_opt ::= COMMA conslist */ + -3, /* (367) conslist ::= conslist tconscomma tcons */ + -1, /* (368) conslist ::= tcons */ + 0, /* (369) tconscomma ::= */ + -1, /* (370) defer_subclause_opt ::= defer_subclause */ + -1, /* (371) resolvetype ::= raisetype */ + -1, /* (372) selectnowith ::= oneselect */ + -1, /* (373) oneselect ::= values */ + -2, /* (374) sclp ::= selcollist COMMA */ + -1, /* (375) as ::= ID|STRING */ + -1, /* (376) indexed_opt ::= indexed_by */ + 0, /* (377) returning ::= */ + -1, /* (378) expr ::= term */ + -1, /* (379) likeop ::= LIKE_KW|MATCH */ + -1, /* (380) case_operand ::= expr */ + -1, /* (381) exprlist ::= nexprlist */ + -1, /* (382) nmnum ::= plus_num */ + -1, /* (383) nmnum ::= nm */ + -1, /* (384) nmnum ::= ON */ + -1, /* (385) nmnum ::= DELETE */ + -1, /* (386) nmnum ::= DEFAULT */ + -1, /* (387) plus_num ::= INTEGER|FLOAT */ + 0, /* (388) foreach_clause ::= */ + -3, /* (389) foreach_clause ::= FOR EACH ROW */ + -1, /* (390) trnm ::= nm */ + 0, /* (391) tridxby ::= */ + -1, /* (392) database_kw_opt ::= DATABASE */ + 0, /* (393) database_kw_opt ::= */ + 0, /* (394) kwcolumn_opt ::= */ + -1, /* (395) kwcolumn_opt ::= COLUMNKW */ + -1, /* (396) vtabarglist ::= vtabarg */ + -3, /* (397) vtabarglist ::= vtabarglist COMMA vtabarg */ + -2, /* (398) vtabarg ::= vtabarg vtabargtoken */ + 0, /* (399) anylist ::= */ + -4, /* (400) anylist ::= anylist LP anylist RP */ + -2, /* (401) anylist ::= anylist ANY */ + 0, /* (402) with ::= */ + -1, /* (403) windowdefn_list ::= windowdefn */ + -1, /* (404) window ::= frame_opt */ }; static void yy_accept(yyParser*); /* Forward Declaration */ @@ -172820,7 +174512,7 @@ static YYACTIONTYPE yy_reduce( case 5: /* transtype ::= DEFERRED */ case 6: /* transtype ::= IMMEDIATE */ yytestcase(yyruleno==6); case 7: /* transtype ::= EXCLUSIVE */ yytestcase(yyruleno==7); - case 319: /* range_or_rows ::= RANGE|ROWS|GROUPS */ yytestcase(yyruleno==319); + case 321: /* range_or_rows ::= RANGE|ROWS|GROUPS */ yytestcase(yyruleno==321); {yymsp[0].minor.yy394 = yymsp[0].major; /*A-overwrites-X*/} break; case 8: /* cmd ::= COMMIT|END trans_opt */ @@ -172857,7 +174549,7 @@ static YYACTIONTYPE yy_reduce( case 72: /* defer_subclause_opt ::= */ yytestcase(yyruleno==72); case 81: /* ifexists ::= */ yytestcase(yyruleno==81); case 98: /* distinct ::= */ yytestcase(yyruleno==98); - case 242: /* collate ::= */ yytestcase(yyruleno==242); + case 244: /* collate ::= */ yytestcase(yyruleno==244); {yymsp[1].minor.yy394 = 0;} break; case 16: /* ifnotexists ::= IF NOT EXISTS */ @@ -173041,9 +174733,9 @@ static YYACTIONTYPE yy_reduce( break; case 63: /* init_deferred_pred_opt ::= INITIALLY DEFERRED */ case 80: /* ifexists ::= IF EXISTS */ yytestcase(yyruleno==80); - case 215: /* between_op ::= NOT BETWEEN */ yytestcase(yyruleno==215); - case 218: /* in_op ::= NOT IN */ yytestcase(yyruleno==218); - case 243: /* collate ::= COLLATE ID|STRING */ yytestcase(yyruleno==243); + case 217: /* between_op ::= NOT BETWEEN */ yytestcase(yyruleno==217); + case 220: /* in_op ::= NOT IN */ yytestcase(yyruleno==220); + case 245: /* collate ::= COLLATE ID|STRING */ yytestcase(yyruleno==245); {yymsp[-1].minor.yy394 = 1;} break; case 64: /* init_deferred_pred_opt ::= INITIALLY IMMEDIATE */ @@ -173192,9 +174884,9 @@ static YYACTIONTYPE yy_reduce( case 99: /* sclp ::= */ case 132: /* orderby_opt ::= */ yytestcase(yyruleno==132); case 142: /* groupby_opt ::= */ yytestcase(yyruleno==142); - case 230: /* exprlist ::= */ yytestcase(yyruleno==230); - case 233: /* paren_exprlist ::= */ yytestcase(yyruleno==233); - case 238: /* eidlist_opt ::= */ yytestcase(yyruleno==238); + case 232: /* exprlist ::= */ yytestcase(yyruleno==232); + case 235: /* paren_exprlist ::= */ yytestcase(yyruleno==235); + case 240: /* eidlist_opt ::= */ yytestcase(yyruleno==240); {yymsp[1].minor.yy322 = 0;} break; case 100: /* selcollist ::= sclp scanpt expr scanpt as */ @@ -173223,8 +174915,8 @@ static YYACTIONTYPE yy_reduce( break; case 103: /* as ::= AS nm */ case 115: /* dbnm ::= DOT nm */ yytestcase(yyruleno==115); - case 254: /* plus_num ::= PLUS INTEGER|FLOAT */ yytestcase(yyruleno==254); - case 255: /* minus_num ::= MINUS INTEGER|FLOAT */ yytestcase(yyruleno==255); + case 256: /* plus_num ::= PLUS INTEGER|FLOAT */ yytestcase(yyruleno==256); + case 257: /* minus_num ::= MINUS INTEGER|FLOAT */ yytestcase(yyruleno==257); {yymsp[-1].minor.yy0 = yymsp[0].minor.yy0;} break; case 105: /* from ::= */ @@ -173396,16 +175088,16 @@ static YYACTIONTYPE yy_reduce( case 146: /* limit_opt ::= */ yytestcase(yyruleno==146); case 151: /* where_opt ::= */ yytestcase(yyruleno==151); case 153: /* where_opt_ret ::= */ yytestcase(yyruleno==153); - case 228: /* case_else ::= */ yytestcase(yyruleno==228); - case 229: /* case_operand ::= */ yytestcase(yyruleno==229); - case 248: /* vinto ::= */ yytestcase(yyruleno==248); + case 230: /* case_else ::= */ yytestcase(yyruleno==230); + case 231: /* case_operand ::= */ yytestcase(yyruleno==231); + case 250: /* vinto ::= */ yytestcase(yyruleno==250); {yymsp[1].minor.yy528 = 0;} break; case 145: /* having_opt ::= HAVING expr */ case 152: /* where_opt ::= WHERE expr */ yytestcase(yyruleno==152); case 154: /* where_opt_ret ::= WHERE expr */ yytestcase(yyruleno==154); - case 227: /* case_else ::= ELSE expr */ yytestcase(yyruleno==227); - case 247: /* vinto ::= INTO expr */ yytestcase(yyruleno==247); + case 229: /* case_else ::= ELSE expr */ yytestcase(yyruleno==229); + case 249: /* vinto ::= INTO expr */ yytestcase(yyruleno==249); {yymsp[-1].minor.yy528 = yymsp[0].minor.yy528;} break; case 147: /* limit_opt ::= LIMIT expr */ @@ -173591,33 +175283,48 @@ static YYACTIONTYPE yy_reduce( } yymsp[-4].minor.yy528 = yylhsminor.yy528; break; - case 188: /* expr ::= ID|INDEXED|JOIN_KW LP STAR RP */ + case 188: /* expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP */ +{ + yylhsminor.yy528 = sqlite3ExprFunction(pParse, yymsp[-4].minor.yy322, &yymsp[-7].minor.yy0, yymsp[-5].minor.yy394); + sqlite3ExprAddFunctionOrderBy(pParse, yylhsminor.yy528, yymsp[-1].minor.yy322); +} + yymsp[-7].minor.yy528 = yylhsminor.yy528; + break; + case 189: /* expr ::= ID|INDEXED|JOIN_KW LP STAR RP */ { yylhsminor.yy528 = sqlite3ExprFunction(pParse, 0, &yymsp[-3].minor.yy0, 0); } yymsp[-3].minor.yy528 = yylhsminor.yy528; break; - case 189: /* expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP filter_over */ + case 190: /* expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP filter_over */ { yylhsminor.yy528 = sqlite3ExprFunction(pParse, yymsp[-2].minor.yy322, &yymsp[-5].minor.yy0, yymsp[-3].minor.yy394); sqlite3WindowAttach(pParse, yylhsminor.yy528, yymsp[0].minor.yy41); } yymsp[-5].minor.yy528 = yylhsminor.yy528; break; - case 190: /* expr ::= ID|INDEXED|JOIN_KW LP STAR RP filter_over */ + case 191: /* expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP filter_over */ +{ + yylhsminor.yy528 = sqlite3ExprFunction(pParse, yymsp[-5].minor.yy322, &yymsp[-8].minor.yy0, yymsp[-6].minor.yy394); + sqlite3WindowAttach(pParse, yylhsminor.yy528, yymsp[0].minor.yy41); + sqlite3ExprAddFunctionOrderBy(pParse, yylhsminor.yy528, yymsp[-2].minor.yy322); +} + yymsp[-8].minor.yy528 = yylhsminor.yy528; + break; + case 192: /* expr ::= ID|INDEXED|JOIN_KW LP STAR RP filter_over */ { yylhsminor.yy528 = sqlite3ExprFunction(pParse, 0, &yymsp[-4].minor.yy0, 0); sqlite3WindowAttach(pParse, yylhsminor.yy528, yymsp[0].minor.yy41); } yymsp[-4].minor.yy528 = yylhsminor.yy528; break; - case 191: /* term ::= CTIME_KW */ + case 193: /* term ::= CTIME_KW */ { yylhsminor.yy528 = sqlite3ExprFunction(pParse, 0, &yymsp[0].minor.yy0, 0); } yymsp[0].minor.yy528 = yylhsminor.yy528; break; - case 192: /* expr ::= LP nexprlist COMMA expr RP */ + case 194: /* expr ::= LP nexprlist COMMA expr RP */ { ExprList *pList = sqlite3ExprListAppend(pParse, yymsp[-3].minor.yy322, yymsp[-1].minor.yy528); yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_VECTOR, 0, 0); @@ -173631,22 +175338,22 @@ static YYACTIONTYPE yy_reduce( } } break; - case 193: /* expr ::= expr AND expr */ + case 195: /* expr ::= expr AND expr */ {yymsp[-2].minor.yy528=sqlite3ExprAnd(pParse,yymsp[-2].minor.yy528,yymsp[0].minor.yy528);} break; - case 194: /* expr ::= expr OR expr */ - case 195: /* expr ::= expr LT|GT|GE|LE expr */ yytestcase(yyruleno==195); - case 196: /* expr ::= expr EQ|NE expr */ yytestcase(yyruleno==196); - case 197: /* expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ yytestcase(yyruleno==197); - case 198: /* expr ::= expr PLUS|MINUS expr */ yytestcase(yyruleno==198); - case 199: /* expr ::= expr STAR|SLASH|REM expr */ yytestcase(yyruleno==199); - case 200: /* expr ::= expr CONCAT expr */ yytestcase(yyruleno==200); + case 196: /* expr ::= expr OR expr */ + case 197: /* expr ::= expr LT|GT|GE|LE expr */ yytestcase(yyruleno==197); + case 198: /* expr ::= expr EQ|NE expr */ yytestcase(yyruleno==198); + case 199: /* expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ yytestcase(yyruleno==199); + case 200: /* expr ::= expr PLUS|MINUS expr */ yytestcase(yyruleno==200); + case 201: /* expr ::= expr STAR|SLASH|REM expr */ yytestcase(yyruleno==201); + case 202: /* expr ::= expr CONCAT expr */ yytestcase(yyruleno==202); {yymsp[-2].minor.yy528=sqlite3PExpr(pParse,yymsp[-1].major,yymsp[-2].minor.yy528,yymsp[0].minor.yy528);} break; - case 201: /* likeop ::= NOT LIKE_KW|MATCH */ + case 203: /* likeop ::= NOT LIKE_KW|MATCH */ {yymsp[-1].minor.yy0=yymsp[0].minor.yy0; yymsp[-1].minor.yy0.n|=0x80000000; /*yymsp[-1].minor.yy0-overwrite-yymsp[0].minor.yy0*/} break; - case 202: /* expr ::= expr likeop expr */ + case 204: /* expr ::= expr likeop expr */ { ExprList *pList; int bNot = yymsp[-1].minor.yy0.n & 0x80000000; @@ -173658,7 +175365,7 @@ static YYACTIONTYPE yy_reduce( if( yymsp[-2].minor.yy528 ) yymsp[-2].minor.yy528->flags |= EP_InfixFunc; } break; - case 203: /* expr ::= expr likeop expr ESCAPE expr */ + case 205: /* expr ::= expr likeop expr ESCAPE expr */ { ExprList *pList; int bNot = yymsp[-3].minor.yy0.n & 0x80000000; @@ -173671,47 +175378,47 @@ static YYACTIONTYPE yy_reduce( if( yymsp[-4].minor.yy528 ) yymsp[-4].minor.yy528->flags |= EP_InfixFunc; } break; - case 204: /* expr ::= expr ISNULL|NOTNULL */ + case 206: /* expr ::= expr ISNULL|NOTNULL */ {yymsp[-1].minor.yy528 = sqlite3PExpr(pParse,yymsp[0].major,yymsp[-1].minor.yy528,0);} break; - case 205: /* expr ::= expr NOT NULL */ + case 207: /* expr ::= expr NOT NULL */ {yymsp[-2].minor.yy528 = sqlite3PExpr(pParse,TK_NOTNULL,yymsp[-2].minor.yy528,0);} break; - case 206: /* expr ::= expr IS expr */ + case 208: /* expr ::= expr IS expr */ { yymsp[-2].minor.yy528 = sqlite3PExpr(pParse,TK_IS,yymsp[-2].minor.yy528,yymsp[0].minor.yy528); binaryToUnaryIfNull(pParse, yymsp[0].minor.yy528, yymsp[-2].minor.yy528, TK_ISNULL); } break; - case 207: /* expr ::= expr IS NOT expr */ + case 209: /* expr ::= expr IS NOT expr */ { yymsp[-3].minor.yy528 = sqlite3PExpr(pParse,TK_ISNOT,yymsp[-3].minor.yy528,yymsp[0].minor.yy528); binaryToUnaryIfNull(pParse, yymsp[0].minor.yy528, yymsp[-3].minor.yy528, TK_NOTNULL); } break; - case 208: /* expr ::= expr IS NOT DISTINCT FROM expr */ + case 210: /* expr ::= expr IS NOT DISTINCT FROM expr */ { yymsp[-5].minor.yy528 = sqlite3PExpr(pParse,TK_IS,yymsp[-5].minor.yy528,yymsp[0].minor.yy528); binaryToUnaryIfNull(pParse, yymsp[0].minor.yy528, yymsp[-5].minor.yy528, TK_ISNULL); } break; - case 209: /* expr ::= expr IS DISTINCT FROM expr */ + case 211: /* expr ::= expr IS DISTINCT FROM expr */ { yymsp[-4].minor.yy528 = sqlite3PExpr(pParse,TK_ISNOT,yymsp[-4].minor.yy528,yymsp[0].minor.yy528); binaryToUnaryIfNull(pParse, yymsp[0].minor.yy528, yymsp[-4].minor.yy528, TK_NOTNULL); } break; - case 210: /* expr ::= NOT expr */ - case 211: /* expr ::= BITNOT expr */ yytestcase(yyruleno==211); + case 212: /* expr ::= NOT expr */ + case 213: /* expr ::= BITNOT expr */ yytestcase(yyruleno==213); {yymsp[-1].minor.yy528 = sqlite3PExpr(pParse, yymsp[-1].major, yymsp[0].minor.yy528, 0);/*A-overwrites-B*/} break; - case 212: /* expr ::= PLUS|MINUS expr */ + case 214: /* expr ::= PLUS|MINUS expr */ { yymsp[-1].minor.yy528 = sqlite3PExpr(pParse, yymsp[-1].major==TK_PLUS ? TK_UPLUS : TK_UMINUS, yymsp[0].minor.yy528, 0); /*A-overwrites-B*/ } break; - case 213: /* expr ::= expr PTR expr */ + case 215: /* expr ::= expr PTR expr */ { ExprList *pList = sqlite3ExprListAppend(pParse, 0, yymsp[-2].minor.yy528); pList = sqlite3ExprListAppend(pParse, pList, yymsp[0].minor.yy528); @@ -173719,11 +175426,11 @@ static YYACTIONTYPE yy_reduce( } yymsp[-2].minor.yy528 = yylhsminor.yy528; break; - case 214: /* between_op ::= BETWEEN */ - case 217: /* in_op ::= IN */ yytestcase(yyruleno==217); + case 216: /* between_op ::= BETWEEN */ + case 219: /* in_op ::= IN */ yytestcase(yyruleno==219); {yymsp[0].minor.yy394 = 0;} break; - case 216: /* expr ::= expr between_op expr AND expr */ + case 218: /* expr ::= expr between_op expr AND expr */ { ExprList *pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy528); pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy528); @@ -173736,7 +175443,7 @@ static YYACTIONTYPE yy_reduce( if( yymsp[-3].minor.yy394 ) yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy528, 0); } break; - case 219: /* expr ::= expr in_op LP exprlist RP */ + case 221: /* expr ::= expr in_op LP exprlist RP */ { if( yymsp[-1].minor.yy322==0 ){ /* Expressions of the form @@ -173782,20 +175489,20 @@ static YYACTIONTYPE yy_reduce( } } break; - case 220: /* expr ::= LP select RP */ + case 222: /* expr ::= LP select RP */ { yymsp[-2].minor.yy528 = sqlite3PExpr(pParse, TK_SELECT, 0, 0); sqlite3PExprAddSelect(pParse, yymsp[-2].minor.yy528, yymsp[-1].minor.yy47); } break; - case 221: /* expr ::= expr in_op LP select RP */ + case 223: /* expr ::= expr in_op LP select RP */ { yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy528, 0); sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy528, yymsp[-1].minor.yy47); if( yymsp[-3].minor.yy394 ) yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy528, 0); } break; - case 222: /* expr ::= expr in_op nm dbnm paren_exprlist */ + case 224: /* expr ::= expr in_op nm dbnm paren_exprlist */ { SrcList *pSrc = sqlite3SrcListAppend(pParse, 0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0); Select *pSelect = sqlite3SelectNew(pParse, 0,pSrc,0,0,0,0,0,0); @@ -173805,14 +175512,14 @@ static YYACTIONTYPE yy_reduce( if( yymsp[-3].minor.yy394 ) yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy528, 0); } break; - case 223: /* expr ::= EXISTS LP select RP */ + case 225: /* expr ::= EXISTS LP select RP */ { Expr *p; p = yymsp[-3].minor.yy528 = sqlite3PExpr(pParse, TK_EXISTS, 0, 0); sqlite3PExprAddSelect(pParse, p, yymsp[-1].minor.yy47); } break; - case 224: /* expr ::= CASE case_operand case_exprlist case_else END */ + case 226: /* expr ::= CASE case_operand case_exprlist case_else END */ { yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_CASE, yymsp[-3].minor.yy528, 0); if( yymsp[-4].minor.yy528 ){ @@ -173824,29 +175531,29 @@ static YYACTIONTYPE yy_reduce( } } break; - case 225: /* case_exprlist ::= case_exprlist WHEN expr THEN expr */ + case 227: /* case_exprlist ::= case_exprlist WHEN expr THEN expr */ { yymsp[-4].minor.yy322 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy322, yymsp[-2].minor.yy528); yymsp[-4].minor.yy322 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy322, yymsp[0].minor.yy528); } break; - case 226: /* case_exprlist ::= WHEN expr THEN expr */ + case 228: /* case_exprlist ::= WHEN expr THEN expr */ { yymsp[-3].minor.yy322 = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy528); yymsp[-3].minor.yy322 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy322, yymsp[0].minor.yy528); } break; - case 231: /* nexprlist ::= nexprlist COMMA expr */ + case 233: /* nexprlist ::= nexprlist COMMA expr */ {yymsp[-2].minor.yy322 = sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy322,yymsp[0].minor.yy528);} break; - case 232: /* nexprlist ::= expr */ + case 234: /* nexprlist ::= expr */ {yymsp[0].minor.yy322 = sqlite3ExprListAppend(pParse,0,yymsp[0].minor.yy528); /*A-overwrites-Y*/} break; - case 234: /* paren_exprlist ::= LP exprlist RP */ - case 239: /* eidlist_opt ::= LP eidlist RP */ yytestcase(yyruleno==239); + case 236: /* paren_exprlist ::= LP exprlist RP */ + case 241: /* eidlist_opt ::= LP eidlist RP */ yytestcase(yyruleno==241); {yymsp[-2].minor.yy322 = yymsp[-1].minor.yy322;} break; - case 235: /* cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ + case 237: /* cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ { sqlite3CreateIndex(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,0), yymsp[-2].minor.yy322, yymsp[-10].minor.yy394, @@ -173856,48 +175563,48 @@ static YYACTIONTYPE yy_reduce( } } break; - case 236: /* uniqueflag ::= UNIQUE */ - case 278: /* raisetype ::= ABORT */ yytestcase(yyruleno==278); + case 238: /* uniqueflag ::= UNIQUE */ + case 280: /* raisetype ::= ABORT */ yytestcase(yyruleno==280); {yymsp[0].minor.yy394 = OE_Abort;} break; - case 237: /* uniqueflag ::= */ + case 239: /* uniqueflag ::= */ {yymsp[1].minor.yy394 = OE_None;} break; - case 240: /* eidlist ::= eidlist COMMA nm collate sortorder */ + case 242: /* eidlist ::= eidlist COMMA nm collate sortorder */ { yymsp[-4].minor.yy322 = parserAddExprIdListTerm(pParse, yymsp[-4].minor.yy322, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy394, yymsp[0].minor.yy394); } break; - case 241: /* eidlist ::= nm collate sortorder */ + case 243: /* eidlist ::= nm collate sortorder */ { yymsp[-2].minor.yy322 = parserAddExprIdListTerm(pParse, 0, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy394, yymsp[0].minor.yy394); /*A-overwrites-Y*/ } break; - case 244: /* cmd ::= DROP INDEX ifexists fullname */ + case 246: /* cmd ::= DROP INDEX ifexists fullname */ {sqlite3DropIndex(pParse, yymsp[0].minor.yy131, yymsp[-1].minor.yy394);} break; - case 245: /* cmd ::= VACUUM vinto */ + case 247: /* cmd ::= VACUUM vinto */ {sqlite3Vacuum(pParse,0,yymsp[0].minor.yy528);} break; - case 246: /* cmd ::= VACUUM nm vinto */ + case 248: /* cmd ::= VACUUM nm vinto */ {sqlite3Vacuum(pParse,&yymsp[-1].minor.yy0,yymsp[0].minor.yy528);} break; - case 249: /* cmd ::= PRAGMA nm dbnm */ + case 251: /* cmd ::= PRAGMA nm dbnm */ {sqlite3Pragma(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,0,0);} break; - case 250: /* cmd ::= PRAGMA nm dbnm EQ nmnum */ + case 252: /* cmd ::= PRAGMA nm dbnm EQ nmnum */ {sqlite3Pragma(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0,0);} break; - case 251: /* cmd ::= PRAGMA nm dbnm LP nmnum RP */ + case 253: /* cmd ::= PRAGMA nm dbnm LP nmnum RP */ {sqlite3Pragma(pParse,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-1].minor.yy0,0);} break; - case 252: /* cmd ::= PRAGMA nm dbnm EQ minus_num */ + case 254: /* cmd ::= PRAGMA nm dbnm EQ minus_num */ {sqlite3Pragma(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0,1);} break; - case 253: /* cmd ::= PRAGMA nm dbnm LP minus_num RP */ + case 255: /* cmd ::= PRAGMA nm dbnm LP minus_num RP */ {sqlite3Pragma(pParse,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-1].minor.yy0,1);} break; - case 256: /* cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ + case 258: /* cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ { Token all; all.z = yymsp[-3].minor.yy0.z; @@ -173905,50 +175612,50 @@ static YYACTIONTYPE yy_reduce( sqlite3FinishTrigger(pParse, yymsp[-1].minor.yy33, &all); } break; - case 257: /* trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ + case 259: /* trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ { sqlite3BeginTrigger(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, yymsp[-5].minor.yy394, yymsp[-4].minor.yy180.a, yymsp[-4].minor.yy180.b, yymsp[-2].minor.yy131, yymsp[0].minor.yy528, yymsp[-10].minor.yy394, yymsp[-8].minor.yy394); yymsp[-10].minor.yy0 = (yymsp[-6].minor.yy0.n==0?yymsp[-7].minor.yy0:yymsp[-6].minor.yy0); /*A-overwrites-T*/ } break; - case 258: /* trigger_time ::= BEFORE|AFTER */ + case 260: /* trigger_time ::= BEFORE|AFTER */ { yymsp[0].minor.yy394 = yymsp[0].major; /*A-overwrites-X*/ } break; - case 259: /* trigger_time ::= INSTEAD OF */ + case 261: /* trigger_time ::= INSTEAD OF */ { yymsp[-1].minor.yy394 = TK_INSTEAD;} break; - case 260: /* trigger_time ::= */ + case 262: /* trigger_time ::= */ { yymsp[1].minor.yy394 = TK_BEFORE; } break; - case 261: /* trigger_event ::= DELETE|INSERT */ - case 262: /* trigger_event ::= UPDATE */ yytestcase(yyruleno==262); + case 263: /* trigger_event ::= DELETE|INSERT */ + case 264: /* trigger_event ::= UPDATE */ yytestcase(yyruleno==264); {yymsp[0].minor.yy180.a = yymsp[0].major; /*A-overwrites-X*/ yymsp[0].minor.yy180.b = 0;} break; - case 263: /* trigger_event ::= UPDATE OF idlist */ + case 265: /* trigger_event ::= UPDATE OF idlist */ {yymsp[-2].minor.yy180.a = TK_UPDATE; yymsp[-2].minor.yy180.b = yymsp[0].minor.yy254;} break; - case 264: /* when_clause ::= */ - case 283: /* key_opt ::= */ yytestcase(yyruleno==283); + case 266: /* when_clause ::= */ + case 285: /* key_opt ::= */ yytestcase(yyruleno==285); { yymsp[1].minor.yy528 = 0; } break; - case 265: /* when_clause ::= WHEN expr */ - case 284: /* key_opt ::= KEY expr */ yytestcase(yyruleno==284); + case 267: /* when_clause ::= WHEN expr */ + case 286: /* key_opt ::= KEY expr */ yytestcase(yyruleno==286); { yymsp[-1].minor.yy528 = yymsp[0].minor.yy528; } break; - case 266: /* trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ + case 268: /* trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ { assert( yymsp[-2].minor.yy33!=0 ); yymsp[-2].minor.yy33->pLast->pNext = yymsp[-1].minor.yy33; yymsp[-2].minor.yy33->pLast = yymsp[-1].minor.yy33; } break; - case 267: /* trigger_cmd_list ::= trigger_cmd SEMI */ + case 269: /* trigger_cmd_list ::= trigger_cmd SEMI */ { assert( yymsp[-1].minor.yy33!=0 ); yymsp[-1].minor.yy33->pLast = yymsp[-1].minor.yy33; } break; - case 268: /* trnm ::= nm DOT nm */ + case 270: /* trnm ::= nm DOT nm */ { yymsp[-2].minor.yy0 = yymsp[0].minor.yy0; sqlite3ErrorMsg(pParse, @@ -173956,39 +175663,39 @@ static YYACTIONTYPE yy_reduce( "statements within triggers"); } break; - case 269: /* tridxby ::= INDEXED BY nm */ + case 271: /* tridxby ::= INDEXED BY nm */ { sqlite3ErrorMsg(pParse, "the INDEXED BY clause is not allowed on UPDATE or DELETE statements " "within triggers"); } break; - case 270: /* tridxby ::= NOT INDEXED */ + case 272: /* tridxby ::= NOT INDEXED */ { sqlite3ErrorMsg(pParse, "the NOT INDEXED clause is not allowed on UPDATE or DELETE statements " "within triggers"); } break; - case 271: /* trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */ + case 273: /* trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */ {yylhsminor.yy33 = sqlite3TriggerUpdateStep(pParse, &yymsp[-6].minor.yy0, yymsp[-2].minor.yy131, yymsp[-3].minor.yy322, yymsp[-1].minor.yy528, yymsp[-7].minor.yy394, yymsp[-8].minor.yy0.z, yymsp[0].minor.yy522);} yymsp[-8].minor.yy33 = yylhsminor.yy33; break; - case 272: /* trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */ + case 274: /* trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */ { yylhsminor.yy33 = sqlite3TriggerInsertStep(pParse,&yymsp[-4].minor.yy0,yymsp[-3].minor.yy254,yymsp[-2].minor.yy47,yymsp[-6].minor.yy394,yymsp[-1].minor.yy444,yymsp[-7].minor.yy522,yymsp[0].minor.yy522);/*yylhsminor.yy33-overwrites-yymsp[-6].minor.yy394*/ } yymsp[-7].minor.yy33 = yylhsminor.yy33; break; - case 273: /* trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ + case 275: /* trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ {yylhsminor.yy33 = sqlite3TriggerDeleteStep(pParse, &yymsp[-3].minor.yy0, yymsp[-1].minor.yy528, yymsp[-5].minor.yy0.z, yymsp[0].minor.yy522);} yymsp[-5].minor.yy33 = yylhsminor.yy33; break; - case 274: /* trigger_cmd ::= scanpt select scanpt */ + case 276: /* trigger_cmd ::= scanpt select scanpt */ {yylhsminor.yy33 = sqlite3TriggerSelectStep(pParse->db, yymsp[-1].minor.yy47, yymsp[-2].minor.yy522, yymsp[0].minor.yy522); /*yylhsminor.yy33-overwrites-yymsp[-1].minor.yy47*/} yymsp[-2].minor.yy33 = yylhsminor.yy33; break; - case 275: /* expr ::= RAISE LP IGNORE RP */ + case 277: /* expr ::= RAISE LP IGNORE RP */ { yymsp[-3].minor.yy528 = sqlite3PExpr(pParse, TK_RAISE, 0, 0); if( yymsp[-3].minor.yy528 ){ @@ -173996,7 +175703,7 @@ static YYACTIONTYPE yy_reduce( } } break; - case 276: /* expr ::= RAISE LP raisetype COMMA nm RP */ + case 278: /* expr ::= RAISE LP raisetype COMMA nm RP */ { yymsp[-5].minor.yy528 = sqlite3ExprAlloc(pParse->db, TK_RAISE, &yymsp[-1].minor.yy0, 1); if( yymsp[-5].minor.yy528 ) { @@ -174004,114 +175711,114 @@ static YYACTIONTYPE yy_reduce( } } break; - case 277: /* raisetype ::= ROLLBACK */ + case 279: /* raisetype ::= ROLLBACK */ {yymsp[0].minor.yy394 = OE_Rollback;} break; - case 279: /* raisetype ::= FAIL */ + case 281: /* raisetype ::= FAIL */ {yymsp[0].minor.yy394 = OE_Fail;} break; - case 280: /* cmd ::= DROP TRIGGER ifexists fullname */ + case 282: /* cmd ::= DROP TRIGGER ifexists fullname */ { sqlite3DropTrigger(pParse,yymsp[0].minor.yy131,yymsp[-1].minor.yy394); } break; - case 281: /* cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ + case 283: /* cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ { sqlite3Attach(pParse, yymsp[-3].minor.yy528, yymsp[-1].minor.yy528, yymsp[0].minor.yy528); } break; - case 282: /* cmd ::= DETACH database_kw_opt expr */ + case 284: /* cmd ::= DETACH database_kw_opt expr */ { sqlite3Detach(pParse, yymsp[0].minor.yy528); } break; - case 285: /* cmd ::= REINDEX */ + case 287: /* cmd ::= REINDEX */ {sqlite3Reindex(pParse, 0, 0);} break; - case 286: /* cmd ::= REINDEX nm dbnm */ + case 288: /* cmd ::= REINDEX nm dbnm */ {sqlite3Reindex(pParse, &yymsp[-1].minor.yy0, &yymsp[0].minor.yy0);} break; - case 287: /* cmd ::= ANALYZE */ + case 289: /* cmd ::= ANALYZE */ {sqlite3Analyze(pParse, 0, 0);} break; - case 288: /* cmd ::= ANALYZE nm dbnm */ + case 290: /* cmd ::= ANALYZE nm dbnm */ {sqlite3Analyze(pParse, &yymsp[-1].minor.yy0, &yymsp[0].minor.yy0);} break; - case 289: /* cmd ::= ALTER TABLE fullname RENAME TO nm */ + case 291: /* cmd ::= ALTER TABLE fullname RENAME TO nm */ { sqlite3AlterRenameTable(pParse,yymsp[-3].minor.yy131,&yymsp[0].minor.yy0); } break; - case 290: /* cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ + case 292: /* cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ { yymsp[-1].minor.yy0.n = (int)(pParse->sLastToken.z-yymsp[-1].minor.yy0.z) + pParse->sLastToken.n; sqlite3AlterFinishAddColumn(pParse, &yymsp[-1].minor.yy0); } break; - case 291: /* cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */ + case 293: /* cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */ { sqlite3AlterDropColumn(pParse, yymsp[-3].minor.yy131, &yymsp[0].minor.yy0); } break; - case 292: /* add_column_fullname ::= fullname */ + case 294: /* add_column_fullname ::= fullname */ { disableLookaside(pParse); sqlite3AlterBeginAddColumn(pParse, yymsp[0].minor.yy131); } break; - case 293: /* cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ + case 295: /* cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ { sqlite3AlterRenameColumn(pParse, yymsp[-5].minor.yy131, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0); } break; - case 294: /* cmd ::= create_vtab */ + case 296: /* cmd ::= create_vtab */ {sqlite3VtabFinishParse(pParse,0);} break; - case 295: /* cmd ::= create_vtab LP vtabarglist RP */ + case 297: /* cmd ::= create_vtab LP vtabarglist RP */ {sqlite3VtabFinishParse(pParse,&yymsp[0].minor.yy0);} break; - case 296: /* create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ + case 298: /* create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ { sqlite3VtabBeginParse(pParse, &yymsp[-3].minor.yy0, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0, yymsp[-4].minor.yy394); } break; - case 297: /* vtabarg ::= */ + case 299: /* vtabarg ::= */ {sqlite3VtabArgInit(pParse);} break; - case 298: /* vtabargtoken ::= ANY */ - case 299: /* vtabargtoken ::= lp anylist RP */ yytestcase(yyruleno==299); - case 300: /* lp ::= LP */ yytestcase(yyruleno==300); + case 300: /* vtabargtoken ::= ANY */ + case 301: /* vtabargtoken ::= lp anylist RP */ yytestcase(yyruleno==301); + case 302: /* lp ::= LP */ yytestcase(yyruleno==302); {sqlite3VtabArgExtend(pParse,&yymsp[0].minor.yy0);} break; - case 301: /* with ::= WITH wqlist */ - case 302: /* with ::= WITH RECURSIVE wqlist */ yytestcase(yyruleno==302); + case 303: /* with ::= WITH wqlist */ + case 304: /* with ::= WITH RECURSIVE wqlist */ yytestcase(yyruleno==304); { sqlite3WithPush(pParse, yymsp[0].minor.yy521, 1); } break; - case 303: /* wqas ::= AS */ + case 305: /* wqas ::= AS */ {yymsp[0].minor.yy516 = M10d_Any;} break; - case 304: /* wqas ::= AS MATERIALIZED */ + case 306: /* wqas ::= AS MATERIALIZED */ {yymsp[-1].minor.yy516 = M10d_Yes;} break; - case 305: /* wqas ::= AS NOT MATERIALIZED */ + case 307: /* wqas ::= AS NOT MATERIALIZED */ {yymsp[-2].minor.yy516 = M10d_No;} break; - case 306: /* wqitem ::= nm eidlist_opt wqas LP select RP */ + case 308: /* wqitem ::= nm eidlist_opt wqas LP select RP */ { yymsp[-5].minor.yy385 = sqlite3CteNew(pParse, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy322, yymsp[-1].minor.yy47, yymsp[-3].minor.yy516); /*A-overwrites-X*/ } break; - case 307: /* wqlist ::= wqitem */ + case 309: /* wqlist ::= wqitem */ { yymsp[0].minor.yy521 = sqlite3WithAdd(pParse, 0, yymsp[0].minor.yy385); /*A-overwrites-X*/ } break; - case 308: /* wqlist ::= wqlist COMMA wqitem */ + case 310: /* wqlist ::= wqlist COMMA wqitem */ { yymsp[-2].minor.yy521 = sqlite3WithAdd(pParse, yymsp[-2].minor.yy521, yymsp[0].minor.yy385); } break; - case 309: /* windowdefn_list ::= windowdefn_list COMMA windowdefn */ + case 311: /* windowdefn_list ::= windowdefn_list COMMA windowdefn */ { assert( yymsp[0].minor.yy41!=0 ); sqlite3WindowChain(pParse, yymsp[0].minor.yy41, yymsp[-2].minor.yy41); @@ -174120,7 +175827,7 @@ static YYACTIONTYPE yy_reduce( } yymsp[-2].minor.yy41 = yylhsminor.yy41; break; - case 310: /* windowdefn ::= nm AS LP window RP */ + case 312: /* windowdefn ::= nm AS LP window RP */ { if( ALWAYS(yymsp[-1].minor.yy41) ){ yymsp[-1].minor.yy41->zName = sqlite3DbStrNDup(pParse->db, yymsp[-4].minor.yy0.z, yymsp[-4].minor.yy0.n); @@ -174129,83 +175836,83 @@ static YYACTIONTYPE yy_reduce( } yymsp[-4].minor.yy41 = yylhsminor.yy41; break; - case 311: /* window ::= PARTITION BY nexprlist orderby_opt frame_opt */ + case 313: /* window ::= PARTITION BY nexprlist orderby_opt frame_opt */ { yymsp[-4].minor.yy41 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, yymsp[-2].minor.yy322, yymsp[-1].minor.yy322, 0); } break; - case 312: /* window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ + case 314: /* window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ { yylhsminor.yy41 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, yymsp[-2].minor.yy322, yymsp[-1].minor.yy322, &yymsp[-5].minor.yy0); } yymsp[-5].minor.yy41 = yylhsminor.yy41; break; - case 313: /* window ::= ORDER BY sortlist frame_opt */ + case 315: /* window ::= ORDER BY sortlist frame_opt */ { yymsp[-3].minor.yy41 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, 0, yymsp[-1].minor.yy322, 0); } break; - case 314: /* window ::= nm ORDER BY sortlist frame_opt */ + case 316: /* window ::= nm ORDER BY sortlist frame_opt */ { yylhsminor.yy41 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, 0, yymsp[-1].minor.yy322, &yymsp[-4].minor.yy0); } yymsp[-4].minor.yy41 = yylhsminor.yy41; break; - case 315: /* window ::= nm frame_opt */ + case 317: /* window ::= nm frame_opt */ { yylhsminor.yy41 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, 0, 0, &yymsp[-1].minor.yy0); } yymsp[-1].minor.yy41 = yylhsminor.yy41; break; - case 316: /* frame_opt ::= */ + case 318: /* frame_opt ::= */ { yymsp[1].minor.yy41 = sqlite3WindowAlloc(pParse, 0, TK_UNBOUNDED, 0, TK_CURRENT, 0, 0); } break; - case 317: /* frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ + case 319: /* frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ { yylhsminor.yy41 = sqlite3WindowAlloc(pParse, yymsp[-2].minor.yy394, yymsp[-1].minor.yy595.eType, yymsp[-1].minor.yy595.pExpr, TK_CURRENT, 0, yymsp[0].minor.yy516); } yymsp[-2].minor.yy41 = yylhsminor.yy41; break; - case 318: /* frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ + case 320: /* frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ { yylhsminor.yy41 = sqlite3WindowAlloc(pParse, yymsp[-5].minor.yy394, yymsp[-3].minor.yy595.eType, yymsp[-3].minor.yy595.pExpr, yymsp[-1].minor.yy595.eType, yymsp[-1].minor.yy595.pExpr, yymsp[0].minor.yy516); } yymsp[-5].minor.yy41 = yylhsminor.yy41; break; - case 320: /* frame_bound_s ::= frame_bound */ - case 322: /* frame_bound_e ::= frame_bound */ yytestcase(yyruleno==322); + case 322: /* frame_bound_s ::= frame_bound */ + case 324: /* frame_bound_e ::= frame_bound */ yytestcase(yyruleno==324); {yylhsminor.yy595 = yymsp[0].minor.yy595;} yymsp[0].minor.yy595 = yylhsminor.yy595; break; - case 321: /* frame_bound_s ::= UNBOUNDED PRECEDING */ - case 323: /* frame_bound_e ::= UNBOUNDED FOLLOWING */ yytestcase(yyruleno==323); - case 325: /* frame_bound ::= CURRENT ROW */ yytestcase(yyruleno==325); + case 323: /* frame_bound_s ::= UNBOUNDED PRECEDING */ + case 325: /* frame_bound_e ::= UNBOUNDED FOLLOWING */ yytestcase(yyruleno==325); + case 327: /* frame_bound ::= CURRENT ROW */ yytestcase(yyruleno==327); {yylhsminor.yy595.eType = yymsp[-1].major; yylhsminor.yy595.pExpr = 0;} yymsp[-1].minor.yy595 = yylhsminor.yy595; break; - case 324: /* frame_bound ::= expr PRECEDING|FOLLOWING */ + case 326: /* frame_bound ::= expr PRECEDING|FOLLOWING */ {yylhsminor.yy595.eType = yymsp[0].major; yylhsminor.yy595.pExpr = yymsp[-1].minor.yy528;} yymsp[-1].minor.yy595 = yylhsminor.yy595; break; - case 326: /* frame_exclude_opt ::= */ + case 328: /* frame_exclude_opt ::= */ {yymsp[1].minor.yy516 = 0;} break; - case 327: /* frame_exclude_opt ::= EXCLUDE frame_exclude */ + case 329: /* frame_exclude_opt ::= EXCLUDE frame_exclude */ {yymsp[-1].minor.yy516 = yymsp[0].minor.yy516;} break; - case 328: /* frame_exclude ::= NO OTHERS */ - case 329: /* frame_exclude ::= CURRENT ROW */ yytestcase(yyruleno==329); + case 330: /* frame_exclude ::= NO OTHERS */ + case 331: /* frame_exclude ::= CURRENT ROW */ yytestcase(yyruleno==331); {yymsp[-1].minor.yy516 = yymsp[-1].major; /*A-overwrites-X*/} break; - case 330: /* frame_exclude ::= GROUP|TIES */ + case 332: /* frame_exclude ::= GROUP|TIES */ {yymsp[0].minor.yy516 = yymsp[0].major; /*A-overwrites-X*/} break; - case 331: /* window_clause ::= WINDOW windowdefn_list */ + case 333: /* window_clause ::= WINDOW windowdefn_list */ { yymsp[-1].minor.yy41 = yymsp[0].minor.yy41; } break; - case 332: /* filter_over ::= filter_clause over_clause */ + case 334: /* filter_over ::= filter_clause over_clause */ { if( yymsp[0].minor.yy41 ){ yymsp[0].minor.yy41->pFilter = yymsp[-1].minor.yy528; @@ -174216,13 +175923,13 @@ static YYACTIONTYPE yy_reduce( } yymsp[-1].minor.yy41 = yylhsminor.yy41; break; - case 333: /* filter_over ::= over_clause */ + case 335: /* filter_over ::= over_clause */ { yylhsminor.yy41 = yymsp[0].minor.yy41; } yymsp[0].minor.yy41 = yylhsminor.yy41; break; - case 334: /* filter_over ::= filter_clause */ + case 336: /* filter_over ::= filter_clause */ { yylhsminor.yy41 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window)); if( yylhsminor.yy41 ){ @@ -174234,13 +175941,13 @@ static YYACTIONTYPE yy_reduce( } yymsp[0].minor.yy41 = yylhsminor.yy41; break; - case 335: /* over_clause ::= OVER LP window RP */ + case 337: /* over_clause ::= OVER LP window RP */ { yymsp[-3].minor.yy41 = yymsp[-1].minor.yy41; assert( yymsp[-3].minor.yy41!=0 ); } break; - case 336: /* over_clause ::= OVER nm */ + case 338: /* over_clause ::= OVER nm */ { yymsp[-1].minor.yy41 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window)); if( yymsp[-1].minor.yy41 ){ @@ -174248,75 +175955,75 @@ static YYACTIONTYPE yy_reduce( } } break; - case 337: /* filter_clause ::= FILTER LP WHERE expr RP */ + case 339: /* filter_clause ::= FILTER LP WHERE expr RP */ { yymsp[-4].minor.yy528 = yymsp[-1].minor.yy528; } break; default: - /* (338) input ::= cmdlist */ yytestcase(yyruleno==338); - /* (339) cmdlist ::= cmdlist ecmd */ yytestcase(yyruleno==339); - /* (340) cmdlist ::= ecmd (OPTIMIZED OUT) */ assert(yyruleno!=340); - /* (341) ecmd ::= SEMI */ yytestcase(yyruleno==341); - /* (342) ecmd ::= cmdx SEMI */ yytestcase(yyruleno==342); - /* (343) ecmd ::= explain cmdx SEMI (NEVER REDUCES) */ assert(yyruleno!=343); - /* (344) trans_opt ::= */ yytestcase(yyruleno==344); - /* (345) trans_opt ::= TRANSACTION */ yytestcase(yyruleno==345); - /* (346) trans_opt ::= TRANSACTION nm */ yytestcase(yyruleno==346); - /* (347) savepoint_opt ::= SAVEPOINT */ yytestcase(yyruleno==347); - /* (348) savepoint_opt ::= */ yytestcase(yyruleno==348); - /* (349) cmd ::= create_table create_table_args */ yytestcase(yyruleno==349); - /* (350) table_option_set ::= table_option (OPTIMIZED OUT) */ assert(yyruleno!=350); - /* (351) columnlist ::= columnlist COMMA columnname carglist */ yytestcase(yyruleno==351); - /* (352) columnlist ::= columnname carglist */ yytestcase(yyruleno==352); - /* (353) nm ::= ID|INDEXED|JOIN_KW */ yytestcase(yyruleno==353); - /* (354) nm ::= STRING */ yytestcase(yyruleno==354); - /* (355) typetoken ::= typename */ yytestcase(yyruleno==355); - /* (356) typename ::= ID|STRING */ yytestcase(yyruleno==356); - /* (357) signed ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=357); - /* (358) signed ::= minus_num (OPTIMIZED OUT) */ assert(yyruleno!=358); - /* (359) carglist ::= carglist ccons */ yytestcase(yyruleno==359); - /* (360) carglist ::= */ yytestcase(yyruleno==360); - /* (361) ccons ::= NULL onconf */ yytestcase(yyruleno==361); - /* (362) ccons ::= GENERATED ALWAYS AS generated */ yytestcase(yyruleno==362); - /* (363) ccons ::= AS generated */ yytestcase(yyruleno==363); - /* (364) conslist_opt ::= COMMA conslist */ yytestcase(yyruleno==364); - /* (365) conslist ::= conslist tconscomma tcons */ yytestcase(yyruleno==365); - /* (366) conslist ::= tcons (OPTIMIZED OUT) */ assert(yyruleno!=366); - /* (367) tconscomma ::= */ yytestcase(yyruleno==367); - /* (368) defer_subclause_opt ::= defer_subclause (OPTIMIZED OUT) */ assert(yyruleno!=368); - /* (369) resolvetype ::= raisetype (OPTIMIZED OUT) */ assert(yyruleno!=369); - /* (370) selectnowith ::= oneselect (OPTIMIZED OUT) */ assert(yyruleno!=370); - /* (371) oneselect ::= values */ yytestcase(yyruleno==371); - /* (372) sclp ::= selcollist COMMA */ yytestcase(yyruleno==372); - /* (373) as ::= ID|STRING */ yytestcase(yyruleno==373); - /* (374) indexed_opt ::= indexed_by (OPTIMIZED OUT) */ assert(yyruleno!=374); - /* (375) returning ::= */ yytestcase(yyruleno==375); - /* (376) expr ::= term (OPTIMIZED OUT) */ assert(yyruleno!=376); - /* (377) likeop ::= LIKE_KW|MATCH */ yytestcase(yyruleno==377); - /* (378) case_operand ::= expr */ yytestcase(yyruleno==378); - /* (379) exprlist ::= nexprlist */ yytestcase(yyruleno==379); - /* (380) nmnum ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=380); - /* (381) nmnum ::= nm (OPTIMIZED OUT) */ assert(yyruleno!=381); - /* (382) nmnum ::= ON */ yytestcase(yyruleno==382); - /* (383) nmnum ::= DELETE */ yytestcase(yyruleno==383); - /* (384) nmnum ::= DEFAULT */ yytestcase(yyruleno==384); - /* (385) plus_num ::= INTEGER|FLOAT */ yytestcase(yyruleno==385); - /* (386) foreach_clause ::= */ yytestcase(yyruleno==386); - /* (387) foreach_clause ::= FOR EACH ROW */ yytestcase(yyruleno==387); - /* (388) trnm ::= nm */ yytestcase(yyruleno==388); - /* (389) tridxby ::= */ yytestcase(yyruleno==389); - /* (390) database_kw_opt ::= DATABASE */ yytestcase(yyruleno==390); - /* (391) database_kw_opt ::= */ yytestcase(yyruleno==391); - /* (392) kwcolumn_opt ::= */ yytestcase(yyruleno==392); - /* (393) kwcolumn_opt ::= COLUMNKW */ yytestcase(yyruleno==393); - /* (394) vtabarglist ::= vtabarg */ yytestcase(yyruleno==394); - /* (395) vtabarglist ::= vtabarglist COMMA vtabarg */ yytestcase(yyruleno==395); - /* (396) vtabarg ::= vtabarg vtabargtoken */ yytestcase(yyruleno==396); - /* (397) anylist ::= */ yytestcase(yyruleno==397); - /* (398) anylist ::= anylist LP anylist RP */ yytestcase(yyruleno==398); - /* (399) anylist ::= anylist ANY */ yytestcase(yyruleno==399); - /* (400) with ::= */ yytestcase(yyruleno==400); - /* (401) windowdefn_list ::= windowdefn (OPTIMIZED OUT) */ assert(yyruleno!=401); - /* (402) window ::= frame_opt (OPTIMIZED OUT) */ assert(yyruleno!=402); + /* (340) input ::= cmdlist */ yytestcase(yyruleno==340); + /* (341) cmdlist ::= cmdlist ecmd */ yytestcase(yyruleno==341); + /* (342) cmdlist ::= ecmd (OPTIMIZED OUT) */ assert(yyruleno!=342); + /* (343) ecmd ::= SEMI */ yytestcase(yyruleno==343); + /* (344) ecmd ::= cmdx SEMI */ yytestcase(yyruleno==344); + /* (345) ecmd ::= explain cmdx SEMI (NEVER REDUCES) */ assert(yyruleno!=345); + /* (346) trans_opt ::= */ yytestcase(yyruleno==346); + /* (347) trans_opt ::= TRANSACTION */ yytestcase(yyruleno==347); + /* (348) trans_opt ::= TRANSACTION nm */ yytestcase(yyruleno==348); + /* (349) savepoint_opt ::= SAVEPOINT */ yytestcase(yyruleno==349); + /* (350) savepoint_opt ::= */ yytestcase(yyruleno==350); + /* (351) cmd ::= create_table create_table_args */ yytestcase(yyruleno==351); + /* (352) table_option_set ::= table_option (OPTIMIZED OUT) */ assert(yyruleno!=352); + /* (353) columnlist ::= columnlist COMMA columnname carglist */ yytestcase(yyruleno==353); + /* (354) columnlist ::= columnname carglist */ yytestcase(yyruleno==354); + /* (355) nm ::= ID|INDEXED|JOIN_KW */ yytestcase(yyruleno==355); + /* (356) nm ::= STRING */ yytestcase(yyruleno==356); + /* (357) typetoken ::= typename */ yytestcase(yyruleno==357); + /* (358) typename ::= ID|STRING */ yytestcase(yyruleno==358); + /* (359) signed ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=359); + /* (360) signed ::= minus_num (OPTIMIZED OUT) */ assert(yyruleno!=360); + /* (361) carglist ::= carglist ccons */ yytestcase(yyruleno==361); + /* (362) carglist ::= */ yytestcase(yyruleno==362); + /* (363) ccons ::= NULL onconf */ yytestcase(yyruleno==363); + /* (364) ccons ::= GENERATED ALWAYS AS generated */ yytestcase(yyruleno==364); + /* (365) ccons ::= AS generated */ yytestcase(yyruleno==365); + /* (366) conslist_opt ::= COMMA conslist */ yytestcase(yyruleno==366); + /* (367) conslist ::= conslist tconscomma tcons */ yytestcase(yyruleno==367); + /* (368) conslist ::= tcons (OPTIMIZED OUT) */ assert(yyruleno!=368); + /* (369) tconscomma ::= */ yytestcase(yyruleno==369); + /* (370) defer_subclause_opt ::= defer_subclause (OPTIMIZED OUT) */ assert(yyruleno!=370); + /* (371) resolvetype ::= raisetype (OPTIMIZED OUT) */ assert(yyruleno!=371); + /* (372) selectnowith ::= oneselect (OPTIMIZED OUT) */ assert(yyruleno!=372); + /* (373) oneselect ::= values */ yytestcase(yyruleno==373); + /* (374) sclp ::= selcollist COMMA */ yytestcase(yyruleno==374); + /* (375) as ::= ID|STRING */ yytestcase(yyruleno==375); + /* (376) indexed_opt ::= indexed_by (OPTIMIZED OUT) */ assert(yyruleno!=376); + /* (377) returning ::= */ yytestcase(yyruleno==377); + /* (378) expr ::= term (OPTIMIZED OUT) */ assert(yyruleno!=378); + /* (379) likeop ::= LIKE_KW|MATCH */ yytestcase(yyruleno==379); + /* (380) case_operand ::= expr */ yytestcase(yyruleno==380); + /* (381) exprlist ::= nexprlist */ yytestcase(yyruleno==381); + /* (382) nmnum ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=382); + /* (383) nmnum ::= nm (OPTIMIZED OUT) */ assert(yyruleno!=383); + /* (384) nmnum ::= ON */ yytestcase(yyruleno==384); + /* (385) nmnum ::= DELETE */ yytestcase(yyruleno==385); + /* (386) nmnum ::= DEFAULT */ yytestcase(yyruleno==386); + /* (387) plus_num ::= INTEGER|FLOAT */ yytestcase(yyruleno==387); + /* (388) foreach_clause ::= */ yytestcase(yyruleno==388); + /* (389) foreach_clause ::= FOR EACH ROW */ yytestcase(yyruleno==389); + /* (390) trnm ::= nm */ yytestcase(yyruleno==390); + /* (391) tridxby ::= */ yytestcase(yyruleno==391); + /* (392) database_kw_opt ::= DATABASE */ yytestcase(yyruleno==392); + /* (393) database_kw_opt ::= */ yytestcase(yyruleno==393); + /* (394) kwcolumn_opt ::= */ yytestcase(yyruleno==394); + /* (395) kwcolumn_opt ::= COLUMNKW */ yytestcase(yyruleno==395); + /* (396) vtabarglist ::= vtabarg */ yytestcase(yyruleno==396); + /* (397) vtabarglist ::= vtabarglist COMMA vtabarg */ yytestcase(yyruleno==397); + /* (398) vtabarg ::= vtabarg vtabargtoken */ yytestcase(yyruleno==398); + /* (399) anylist ::= */ yytestcase(yyruleno==399); + /* (400) anylist ::= anylist LP anylist RP */ yytestcase(yyruleno==400); + /* (401) anylist ::= anylist ANY */ yytestcase(yyruleno==401); + /* (402) with ::= */ yytestcase(yyruleno==402); + /* (403) windowdefn_list ::= windowdefn (OPTIMIZED OUT) */ assert(yyruleno!=403); + /* (404) window ::= frame_opt (OPTIMIZED OUT) */ assert(yyruleno!=404); break; /********** End reduce actions ************************************************/ }; @@ -176440,7 +178147,9 @@ SQLITE_PRIVATE int sqlite3Fts5Init(sqlite3*); #ifdef SQLITE_ENABLE_STMTVTAB SQLITE_PRIVATE int sqlite3StmtVtabInit(sqlite3*); #endif - +#ifdef SQLITE_EXTRA_AUTOEXT +int SQLITE_EXTRA_AUTOEXT(sqlite3*); +#endif /* ** An array of pointers to extension initializer functions for ** built-in extensions. @@ -176474,6 +178183,9 @@ static int (*const sqlite3BuiltinExtensions[])(sqlite3*) = { #ifdef SQLITE_ENABLE_BYTECODE_VTAB sqlite3VdbeBytecodeVtabInit, #endif +#ifdef SQLITE_EXTRA_AUTOEXT + SQLITE_EXTRA_AUTOEXT, +#endif }; #ifndef SQLITE_AMALGAMATION @@ -176547,6 +178259,32 @@ SQLITE_API char *sqlite3_temp_directory = 0; */ SQLITE_API char *sqlite3_data_directory = 0; +/* +** Determine whether or not high-precision (long double) floating point +** math works correctly on CPU currently running. +*/ +static SQLITE_NOINLINE int hasHighPrecisionDouble(int rc){ + if( sizeof(LONGDOUBLE_TYPE)<=8 ){ + /* If the size of "long double" is not more than 8, then + ** high-precision math is not possible. */ + return 0; + }else{ + /* Just because sizeof(long double)>8 does not mean that the underlying + ** hardware actually supports high-precision floating point. For example, + ** clearing the 0x100 bit in the floating-point control word on Intel + ** processors will make long double work like double, even though long + ** double takes up more space. The only way to determine if long double + ** actually works is to run an experiment. */ + LONGDOUBLE_TYPE a, b, c; + rc++; + a = 1.0+rc*0.1; + b = 1.0e+18+rc*25.0; + c = a+b; + return b!=c; + } +} + + /* ** Initialize SQLite. ** @@ -176742,6 +178480,12 @@ SQLITE_API int sqlite3_initialize(void){ } #endif + /* Experimentally determine if high-precision floating point is + ** available. */ +#ifndef SQLITE_OMIT_WSD + sqlite3Config.bUseLongDouble = hasHighPrecisionDouble(rc); +#endif + return rc; } @@ -177312,6 +179056,10 @@ SQLITE_API int sqlite3_db_cacheflush(sqlite3 *db){ SQLITE_API int sqlite3_db_config(sqlite3 *db, int op, ...){ va_list ap; int rc; + +#ifdef SQLITE_ENABLE_API_ARMOR + if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT; +#endif sqlite3_mutex_enter(db->mutex); va_start(ap, op); switch( op ){ @@ -177641,6 +179389,14 @@ static int sqlite3Close(sqlite3 *db, int forceZombie){ } #endif + while( db->pDbData ){ + DbClientData *p = db->pDbData; + db->pDbData = p->pNext; + assert( p->pData!=0 ); + if( p->xDestructor ) p->xDestructor(p->pData); + sqlite3_free(p); + } + /* Convert the connection into a zombie and then close it. */ db->eOpenState = SQLITE_STATE_ZOMBIE; @@ -178258,7 +180014,7 @@ SQLITE_PRIVATE int sqlite3CreateFunc( assert( SQLITE_FUNC_CONSTANT==SQLITE_DETERMINISTIC ); assert( SQLITE_FUNC_DIRECT==SQLITE_DIRECTONLY ); extraFlags = enc & (SQLITE_DETERMINISTIC|SQLITE_DIRECTONLY| - SQLITE_SUBTYPE|SQLITE_INNOCUOUS); + SQLITE_SUBTYPE|SQLITE_INNOCUOUS|SQLITE_RESULT_SUBTYPE); enc &= (SQLITE_FUNC_ENCMASK|SQLITE_ANY); /* The SQLITE_INNOCUOUS flag is the same bit as SQLITE_FUNC_UNSAFE. But @@ -178715,6 +180471,12 @@ SQLITE_API void *sqlite3_preupdate_hook( void *pArg /* First callback argument */ ){ void *pRet; + +#ifdef SQLITE_ENABLE_API_ARMOR + if( db==0 ){ + return 0; + } +#endif sqlite3_mutex_enter(db->mutex); pRet = db->pPreUpdateArg; db->xPreUpdateCallback = xCallback; @@ -178861,7 +180623,7 @@ SQLITE_API int sqlite3_wal_checkpoint_v2( if( eModeSQLITE_CHECKPOINT_TRUNCATE ){ /* EVIDENCE-OF: R-03996-12088 The M parameter must be a valid checkpoint ** mode: */ - return SQLITE_MISUSE; + return SQLITE_MISUSE_BKPT; } sqlite3_mutex_enter(db->mutex); @@ -180098,6 +181860,69 @@ SQLITE_API int sqlite3_collation_needed16( } #endif /* SQLITE_OMIT_UTF16 */ +/* +** Find existing client data. +*/ +SQLITE_API void *sqlite3_get_clientdata(sqlite3 *db, const char *zName){ + DbClientData *p; + sqlite3_mutex_enter(db->mutex); + for(p=db->pDbData; p; p=p->pNext){ + if( strcmp(p->zName, zName)==0 ){ + void *pResult = p->pData; + sqlite3_mutex_leave(db->mutex); + return pResult; + } + } + sqlite3_mutex_leave(db->mutex); + return 0; +} + +/* +** Add new client data to a database connection. +*/ +SQLITE_API int sqlite3_set_clientdata( + sqlite3 *db, /* Attach client data to this connection */ + const char *zName, /* Name of the client data */ + void *pData, /* The client data itself */ + void (*xDestructor)(void*) /* Destructor */ +){ + DbClientData *p, **pp; + sqlite3_mutex_enter(db->mutex); + pp = &db->pDbData; + for(p=db->pDbData; p && strcmp(p->zName,zName); p=p->pNext){ + pp = &p->pNext; + } + if( p ){ + assert( p->pData!=0 ); + if( p->xDestructor ) p->xDestructor(p->pData); + if( pData==0 ){ + *pp = p->pNext; + sqlite3_free(p); + sqlite3_mutex_leave(db->mutex); + return SQLITE_OK; + } + }else if( pData==0 ){ + sqlite3_mutex_leave(db->mutex); + return SQLITE_OK; + }else{ + size_t n = strlen(zName); + p = sqlite3_malloc64( sizeof(DbClientData)+n+1 ); + if( p==0 ){ + if( xDestructor ) xDestructor(pData); + sqlite3_mutex_leave(db->mutex); + return SQLITE_NOMEM; + } + memcpy(p->zName, zName, n+1); + p->pNext = db->pDbData; + db->pDbData = p; + } + p->pData = pData; + p->xDestructor = xDestructor; + sqlite3_mutex_leave(db->mutex); + return SQLITE_OK; +} + + #ifndef SQLITE_OMIT_DEPRECATED /* ** This function is now an anachronism. It used to be used to recover from a @@ -180447,6 +182272,28 @@ SQLITE_API int sqlite3_test_control(int op, ...){ } #endif + /* sqlite3_test_control(SQLITE_TESTCTRL_FK_NO_ACTION, sqlite3 *db, int b); + ** + ** If b is true, then activate the SQLITE_FkNoAction setting. If b is + ** false then clearn that setting. If the SQLITE_FkNoAction setting is + ** abled, all foreign key ON DELETE and ON UPDATE actions behave as if + ** they were NO ACTION, regardless of how they are defined. + ** + ** NB: One must usually run "PRAGMA writable_schema=RESET" after + ** using this test-control, before it will take full effect. failing + ** to reset the schema can result in some unexpected behavior. + */ + case SQLITE_TESTCTRL_FK_NO_ACTION: { + sqlite3 *db = va_arg(ap, sqlite3*); + int b = va_arg(ap, int); + if( b ){ + db->flags |= SQLITE_FkNoAction; + }else{ + db->flags &= ~SQLITE_FkNoAction; + } + break; + } + /* ** sqlite3_test_control(BITVEC_TEST, size, program) ** @@ -180871,11 +182718,11 @@ SQLITE_API int sqlite3_test_control(int op, ...){ ** X<0 Make no changes to the bUseLongDouble. Just report value. ** X==0 Disable bUseLongDouble ** X==1 Enable bUseLongDouble - ** X==2 Set bUseLongDouble to its default value for this platform + ** X>=2 Set bUseLongDouble to its default value for this platform */ case SQLITE_TESTCTRL_USELONGDOUBLE: { int b = va_arg(ap, int); - if( b==2 ) b = sizeof(LONGDOUBLE_TYPE)>8; + if( b>=2 ) b = hasHighPrecisionDouble(b); if( b>=0 ) sqlite3Config.bUseLongDouble = b>0; rc = sqlite3Config.bUseLongDouble!=0; break; @@ -180912,6 +182759,28 @@ SQLITE_API int sqlite3_test_control(int op, ...){ break; } #endif + + /* sqlite3_test_control(SQLITE_TESTCTRL_JSON_SELFCHECK, &onOff); + ** + ** Activate or deactivate validation of JSONB that is generated from + ** text. Off by default, as the validation is slow. Validation is + ** only available if compiled using SQLITE_DEBUG. + ** + ** If onOff is initially 1, then turn it on. If onOff is initially + ** off, turn it off. If onOff is initially -1, then change onOff + ** to be the current setting. + */ + case SQLITE_TESTCTRL_JSON_SELFCHECK: { +#if defined(SQLITE_DEBUG) && !defined(SQLITE_OMIT_WSD) + int *pOnOff = va_arg(ap, int*); + if( *pOnOff<0 ){ + *pOnOff = sqlite3Config.bJsonSelfcheck; + }else{ + sqlite3Config.bJsonSelfcheck = (u8)((*pOnOff)&0xff); + } +#endif + break; + } } va_end(ap); #endif /* SQLITE_UNTESTABLE */ @@ -181289,7 +183158,7 @@ SQLITE_API int sqlite3_compileoption_used(const char *zOptName){ int nOpt; const char **azCompileOpt; -#if SQLITE_ENABLE_API_ARMOR +#ifdef SQLITE_ENABLE_API_ARMOR if( zOptName==0 ){ (void)SQLITE_MISUSE_BKPT; return 0; @@ -181484,6 +183353,9 @@ SQLITE_API int sqlite3_unlock_notify( ){ int rc = SQLITE_OK; +#ifdef SQLITE_ENABLE_API_ARMOR + if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT; +#endif sqlite3_mutex_enter(db->mutex); enterMutex(); @@ -182505,6 +184377,7 @@ struct Fts3Table { int nPgsz; /* Page size for host database */ char *zSegmentsTbl; /* Name of %_segments table */ sqlite3_blob *pSegments; /* Blob handle open on %_segments table */ + int iSavepoint; /* ** The following array of hash tables is used to buffer pending index @@ -182892,6 +184765,8 @@ SQLITE_PRIVATE int sqlite3FtsUnicodeIsdiacritic(int); SQLITE_PRIVATE int sqlite3Fts3ExprIterate(Fts3Expr*, int (*x)(Fts3Expr*,int,void*), void*); +SQLITE_PRIVATE int sqlite3Fts3IntegrityCheck(Fts3Table *p, int *pbOk); + #endif /* !SQLITE_CORE || SQLITE_ENABLE_FTS3 */ #endif /* _FTSINT_H */ @@ -183248,6 +185123,7 @@ static void fts3DeclareVtab(int *pRc, Fts3Table *p){ zLanguageid = (p->zLanguageid ? p->zLanguageid : "__langid"); sqlite3_vtab_config(p->db, SQLITE_VTAB_CONSTRAINT_SUPPORT, 1); + sqlite3_vtab_config(p->db, SQLITE_VTAB_INNOCUOUS); /* Create a list of user columns for the virtual table */ zCols = sqlite3_mprintf("%Q, ", p->azColumn[0]); @@ -186497,6 +188373,8 @@ static int fts3RenameMethod( rc = sqlite3Fts3PendingTermsFlush(p); } + p->bIgnoreSavepoint = 1; + if( p->zContentTbl==0 ){ fts3DbExec(&rc, db, "ALTER TABLE %Q.'%q_content' RENAME TO '%q_content';", @@ -186524,6 +188402,8 @@ static int fts3RenameMethod( "ALTER TABLE %Q.'%q_segdir' RENAME TO '%q_segdir';", p->zDb, p->zName, zName ); + + p->bIgnoreSavepoint = 0; return rc; } @@ -186534,12 +188414,28 @@ static int fts3RenameMethod( */ static int fts3SavepointMethod(sqlite3_vtab *pVtab, int iSavepoint){ int rc = SQLITE_OK; - UNUSED_PARAMETER(iSavepoint); - assert( ((Fts3Table *)pVtab)->inTransaction ); - assert( ((Fts3Table *)pVtab)->mxSavepoint <= iSavepoint ); - TESTONLY( ((Fts3Table *)pVtab)->mxSavepoint = iSavepoint ); - if( ((Fts3Table *)pVtab)->bIgnoreSavepoint==0 ){ - rc = fts3SyncMethod(pVtab); + Fts3Table *pTab = (Fts3Table*)pVtab; + assert( pTab->inTransaction ); + assert( pTab->mxSavepoint<=iSavepoint ); + TESTONLY( pTab->mxSavepoint = iSavepoint ); + + if( pTab->bIgnoreSavepoint==0 ){ + if( fts3HashCount(&pTab->aIndex[0].hPending)>0 ){ + char *zSql = sqlite3_mprintf("INSERT INTO %Q.%Q(%Q) VALUES('flush')", + pTab->zDb, pTab->zName, pTab->zName + ); + if( zSql ){ + pTab->bIgnoreSavepoint = 1; + rc = sqlite3_exec(pTab->db, zSql, 0, 0, 0); + pTab->bIgnoreSavepoint = 0; + sqlite3_free(zSql); + }else{ + rc = SQLITE_NOMEM; + } + } + if( rc==SQLITE_OK ){ + pTab->iSavepoint = iSavepoint+1; + } } return rc; } @@ -186550,12 +188446,11 @@ static int fts3SavepointMethod(sqlite3_vtab *pVtab, int iSavepoint){ ** This is a no-op. */ static int fts3ReleaseMethod(sqlite3_vtab *pVtab, int iSavepoint){ - TESTONLY( Fts3Table *p = (Fts3Table*)pVtab ); - UNUSED_PARAMETER(iSavepoint); - UNUSED_PARAMETER(pVtab); - assert( p->inTransaction ); - assert( p->mxSavepoint >= iSavepoint ); - TESTONLY( p->mxSavepoint = iSavepoint-1 ); + Fts3Table *pTab = (Fts3Table*)pVtab; + assert( pTab->inTransaction ); + assert( pTab->mxSavepoint >= iSavepoint ); + TESTONLY( pTab->mxSavepoint = iSavepoint-1 ); + pTab->iSavepoint = iSavepoint; return SQLITE_OK; } @@ -186565,11 +188460,13 @@ static int fts3ReleaseMethod(sqlite3_vtab *pVtab, int iSavepoint){ ** Discard the contents of the pending terms table. */ static int fts3RollbackToMethod(sqlite3_vtab *pVtab, int iSavepoint){ - Fts3Table *p = (Fts3Table*)pVtab; + Fts3Table *pTab = (Fts3Table*)pVtab; UNUSED_PARAMETER(iSavepoint); - assert( p->inTransaction ); - TESTONLY( p->mxSavepoint = iSavepoint ); - sqlite3Fts3PendingTermsClear(p); + assert( pTab->inTransaction ); + TESTONLY( pTab->mxSavepoint = iSavepoint ); + if( (iSavepoint+1)<=pTab->iSavepoint ){ + sqlite3Fts3PendingTermsClear(pTab); + } return SQLITE_OK; } @@ -186588,8 +188485,40 @@ static int fts3ShadowName(const char *zName){ return 0; } +/* +** Implementation of the xIntegrity() method on the FTS3/FTS4 virtual +** table. +*/ +static int fts3IntegrityMethod( + sqlite3_vtab *pVtab, /* The virtual table to be checked */ + const char *zSchema, /* Name of schema in which pVtab lives */ + const char *zTabname, /* Name of the pVTab table */ + int isQuick, /* True if this is a quick_check */ + char **pzErr /* Write error message here */ +){ + Fts3Table *p = (Fts3Table*)pVtab; + int rc; + int bOk = 0; + + UNUSED_PARAMETER(isQuick); + rc = sqlite3Fts3IntegrityCheck(p, &bOk); + assert( rc!=SQLITE_CORRUPT_VTAB || bOk==0 ); + if( rc!=SQLITE_OK && rc!=SQLITE_CORRUPT_VTAB ){ + *pzErr = sqlite3_mprintf("unable to validate the inverted index for" + " FTS%d table %s.%s: %s", + p->bFts4 ? 4 : 3, zSchema, zTabname, sqlite3_errstr(rc)); + }else if( bOk==0 ){ + *pzErr = sqlite3_mprintf("malformed inverted index for FTS%d table %s.%s", + p->bFts4 ? 4 : 3, zSchema, zTabname); + } + sqlite3Fts3SegmentsClose(p); + return SQLITE_OK; +} + + + static const sqlite3_module fts3Module = { - /* iVersion */ 3, + /* iVersion */ 4, /* xCreate */ fts3CreateMethod, /* xConnect */ fts3ConnectMethod, /* xBestIndex */ fts3BestIndexMethod, @@ -186613,6 +188542,7 @@ static const sqlite3_module fts3Module = { /* xRelease */ fts3ReleaseMethod, /* xRollbackTo */ fts3RollbackToMethod, /* xShadowName */ fts3ShadowName, + /* xIntegrity */ fts3IntegrityMethod, }; /* @@ -189288,7 +191218,8 @@ SQLITE_PRIVATE int sqlite3Fts3InitAux(sqlite3 *db){ 0, /* xSavepoint */ 0, /* xRelease */ 0, /* xRollbackTo */ - 0 /* xShadowName */ + 0, /* xShadowName */ + 0 /* xIntegrity */ }; int rc; /* Return code */ @@ -192854,7 +194785,8 @@ SQLITE_PRIVATE int sqlite3Fts3InitTok(sqlite3 *db, Fts3Hash *pHash, void(*xDestr 0, /* xSavepoint */ 0, /* xRelease */ 0, /* xRollbackTo */ - 0 /* xShadowName */ + 0, /* xShadowName */ + 0 /* xIntegrity */ }; int rc; /* Return code */ @@ -196195,7 +198127,6 @@ SQLITE_PRIVATE int sqlite3Fts3PendingTermsFlush(Fts3Table *p){ rc = fts3SegmentMerge(p, p->iPrevLangid, i, FTS3_SEGCURSOR_PENDING); if( rc==SQLITE_DONE ) rc = SQLITE_OK; } - sqlite3Fts3PendingTermsClear(p); /* Determine the auto-incr-merge setting if unknown. If enabled, ** estimate the number of leaf blocks of content to be written @@ -196217,6 +198148,10 @@ SQLITE_PRIVATE int sqlite3Fts3PendingTermsFlush(Fts3Table *p){ rc = sqlite3_reset(pStmt); } } + + if( rc==SQLITE_OK ){ + sqlite3Fts3PendingTermsClear(p); + } return rc; } @@ -196848,6 +198783,8 @@ static int fts3AppendToNode( blobGrowBuffer(pPrev, nTerm, &rc); if( rc!=SQLITE_OK ) return rc; + assert( pPrev!=0 ); + assert( pPrev->a!=0 ); nPrefix = fts3PrefixCompress(pPrev->a, pPrev->n, zTerm, nTerm); nSuffix = nTerm - nPrefix; @@ -196904,9 +198841,13 @@ static int fts3IncrmergeAppend( nSpace += sqlite3Fts3VarintLen(nDoclist) + nDoclist; /* If the current block is not empty, and if adding this term/doclist - ** to the current block would make it larger than Fts3Table.nNodeSize - ** bytes, write this block out to the database. */ - if( pLeaf->block.n>0 && (pLeaf->block.n + nSpace)>p->nNodeSize ){ + ** to the current block would make it larger than Fts3Table.nNodeSize bytes, + ** and if there is still room for another leaf page, write this block out to + ** the database. */ + if( pLeaf->block.n>0 + && (pLeaf->block.n + nSpace)>p->nNodeSize + && pLeaf->iBlock < (pWriter->iStart + pWriter->nLeafEst) + ){ rc = fts3WriteSegment(p, pLeaf->iBlock, pLeaf->block.a, pLeaf->block.n); pWriter->nWork++; @@ -197238,7 +199179,7 @@ static int fts3IncrmergeLoad( rc = sqlite3Fts3ReadBlock(p, reader.iChild, &aBlock, &nBlock,0); blobGrowBuffer(&pNode->block, MAX(nBlock, p->nNodeSize)+FTS3_NODE_PADDING, &rc - ); + ); if( rc==SQLITE_OK ){ memcpy(pNode->block.a, aBlock, nBlock); pNode->block.n = nBlock; @@ -198155,7 +200096,7 @@ static u64 fts3ChecksumIndex( ** If an error occurs (e.g. an OOM or IO error), return an SQLite error ** code. The final value of *pbOk is undefined in this case. */ -static int fts3IntegrityCheck(Fts3Table *p, int *pbOk){ +SQLITE_PRIVATE int sqlite3Fts3IntegrityCheck(Fts3Table *p, int *pbOk){ int rc = SQLITE_OK; /* Return code */ u64 cksum1 = 0; /* Checksum based on FTS index contents */ u64 cksum2 = 0; /* Checksum based on %_content contents */ @@ -198233,7 +200174,7 @@ static int fts3IntegrityCheck(Fts3Table *p, int *pbOk){ sqlite3_finalize(pStmt); } - *pbOk = (cksum1==cksum2); + *pbOk = (rc==SQLITE_OK && cksum1==cksum2); return rc; } @@ -198273,7 +200214,7 @@ static int fts3DoIntegrityCheck( ){ int rc; int bOk = 0; - rc = fts3IntegrityCheck(p, &bOk); + rc = sqlite3Fts3IntegrityCheck(p, &bOk); if( rc==SQLITE_OK && bOk==0 ) rc = FTS_CORRUPT_VTAB; return rc; } @@ -198303,8 +200244,11 @@ static int fts3SpecialInsert(Fts3Table *p, sqlite3_value *pVal){ rc = fts3DoIncrmerge(p, &zVal[6]); }else if( nVal>10 && 0==sqlite3_strnicmp(zVal, "automerge=", 10) ){ rc = fts3DoAutoincrmerge(p, &zVal[10]); + }else if( nVal==5 && 0==sqlite3_strnicmp(zVal, "flush", 5) ){ + rc = sqlite3Fts3PendingTermsFlush(p); + } #if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) - }else{ + else{ int v; if( nVal>9 && 0==sqlite3_strnicmp(zVal, "nodesize=", 9) ){ v = atoi(&zVal[9]); @@ -198322,8 +200266,8 @@ static int fts3SpecialInsert(Fts3Table *p, sqlite3_value *pVal){ if( v>=4 && v<=FTS3_MERGE_COUNT && (v&1)==0 ) p->nMergeCount = v; rc = SQLITE_OK; } -#endif } +#endif return rc; } @@ -201244,24 +203188,145 @@ SQLITE_PRIVATE int sqlite3FtsUnicodeFold(int c, int eRemoveDiacritic){ ** ****************************************************************************** ** -** This SQLite JSON functions. +** SQLite JSON functions. ** ** This file began as an extension in ext/misc/json1.c in 2015. That ** extension proved so useful that it has now been moved into the core. ** -** For the time being, all JSON is stored as pure text. (We might add -** a JSONB type in the future which stores a binary encoding of JSON in -** a BLOB, but there is no support for JSONB in the current implementation. -** This implementation parses JSON text at 250 MB/s, so it is hard to see -** how JSONB might improve on that.) +** The original design stored all JSON as pure text, canonical RFC-8259. +** Support for JSON-5 extensions was added with version 3.42.0 (2023-05-16). +** All generated JSON text still conforms strictly to RFC-8259, but text +** with JSON-5 extensions is accepted as input. +** +** Beginning with version 3.45.0 (circa 2024-01-01), these routines also +** accept BLOB values that have JSON encoded using a binary representation +** called "JSONB". The name JSONB comes from PostgreSQL, however the on-disk +** format SQLite JSONB is completely different and incompatible with +** PostgreSQL JSONB. +** +** Decoding and interpreting JSONB is still O(N) where N is the size of +** the input, the same as text JSON. However, the constant of proportionality +** for JSONB is much smaller due to faster parsing. The size of each +** element in JSONB is encoded in its header, so there is no need to search +** for delimiters using persnickety syntax rules. JSONB seems to be about +** 3x faster than text JSON as a result. JSONB is also tends to be slightly +** smaller than text JSON, by 5% or 10%, but there are corner cases where +** JSONB can be slightly larger. So you are not far mistaken to say that +** a JSONB blob is the same size as the equivalent RFC-8259 text. +** +** +** THE JSONB ENCODING: +** +** Every JSON element is encoded in JSONB as a header and a payload. +** The header is between 1 and 9 bytes in size. The payload is zero +** or more bytes. +** +** The lower 4 bits of the first byte of the header determines the +** element type: +** +** 0: NULL +** 1: TRUE +** 2: FALSE +** 3: INT -- RFC-8259 integer literal +** 4: INT5 -- JSON5 integer literal +** 5: FLOAT -- RFC-8259 floating point literal +** 6: FLOAT5 -- JSON5 floating point literal +** 7: TEXT -- Text literal acceptable to both SQL and JSON +** 8: TEXTJ -- Text containing RFC-8259 escapes +** 9: TEXT5 -- Text containing JSON5 and/or RFC-8259 escapes +** 10: TEXTRAW -- Text containing unescaped syntax characters +** 11: ARRAY +** 12: OBJECT +** +** The other three possible values (13-15) are reserved for future +** enhancements. +** +** The upper 4 bits of the first byte determine the size of the header +** and sometimes also the size of the payload. If X is the first byte +** of the element and if X>>4 is between 0 and 11, then the payload +** will be that many bytes in size and the header is exactly one byte +** in size. Other four values for X>>4 (12-15) indicate that the header +** is more than one byte in size and that the payload size is determined +** by the remainder of the header, interpreted as a unsigned big-endian +** integer. +** +** Value of X>>4 Size integer Total header size +** ------------- -------------------- ----------------- +** 12 1 byte (0-255) 2 +** 13 2 byte (0-65535) 3 +** 14 4 byte (0-4294967295) 5 +** 15 8 byte (0-1.8e19) 9 +** +** The payload size need not be expressed in its minimal form. For example, +** if the payload size is 10, the size can be expressed in any of 5 different +** ways: (1) (X>>4)==10, (2) (X>>4)==12 following by on 0x0a byte, +** (3) (X>>4)==13 followed by 0x00 and 0x0a, (4) (X>>4)==14 followed by +** 0x00 0x00 0x00 0x0a, or (5) (X>>4)==15 followed by 7 bytes of 0x00 and +** a single byte of 0x0a. The shorter forms are preferred, of course, but +** sometimes when generating JSONB, the payload size is not known in advance +** and it is convenient to reserve sufficient header space to cover the +** largest possible payload size and then come back later and patch up +** the size when it becomes known, resulting in a non-minimal encoding. +** +** The value (X>>4)==15 is not actually used in the current implementation +** (as SQLite is currently unable handle BLOBs larger than about 2GB) +** but is included in the design to allow for future enhancements. +** +** The payload follows the header. NULL, TRUE, and FALSE have no payload and +** their payload size must always be zero. The payload for INT, INT5, +** FLOAT, FLOAT5, TEXT, TEXTJ, TEXT5, and TEXTROW is text. Note that the +** "..." or '...' delimiters are omitted from the various text encodings. +** The payload for ARRAY and OBJECT is a list of additional elements that +** are the content for the array or object. The payload for an OBJECT +** must be an even number of elements. The first element of each pair is +** the label and must be of type TEXT, TEXTJ, TEXT5, or TEXTRAW. +** +** A valid JSONB blob consists of a single element, as described above. +** Usually this will be an ARRAY or OBJECT element which has many more +** elements as its content. But the overall blob is just a single element. +** +** Input validation for JSONB blobs simply checks that the element type +** code is between 0 and 12 and that the total size of the element +** (header plus payload) is the same as the size of the BLOB. If those +** checks are true, the BLOB is assumed to be JSONB and processing continues. +** Errors are only raised if some other miscoding is discovered during +** processing. +** +** Additional information can be found in the doc/jsonb.md file of the +** canonical SQLite source tree. */ #ifndef SQLITE_OMIT_JSON /* #include "sqliteInt.h" */ +/* JSONB element types +*/ +#define JSONB_NULL 0 /* "null" */ +#define JSONB_TRUE 1 /* "true" */ +#define JSONB_FALSE 2 /* "false" */ +#define JSONB_INT 3 /* integer acceptable to JSON and SQL */ +#define JSONB_INT5 4 /* integer in 0x000 notation */ +#define JSONB_FLOAT 5 /* float acceptable to JSON and SQL */ +#define JSONB_FLOAT5 6 /* float with JSON5 extensions */ +#define JSONB_TEXT 7 /* Text compatible with both JSON and SQL */ +#define JSONB_TEXTJ 8 /* Text with JSON escapes */ +#define JSONB_TEXT5 9 /* Text with JSON-5 escape */ +#define JSONB_TEXTRAW 10 /* SQL text that needs escaping for JSON */ +#define JSONB_ARRAY 11 /* An array */ +#define JSONB_OBJECT 12 /* An object */ + +/* Human-readable names for the JSONB values. The index for each +** string must correspond to the JSONB_* integer above. +*/ +static const char * const jsonbType[] = { + "null", "true", "false", "integer", "integer", + "real", "real", "text", "text", "text", + "text", "array", "object", "", "", "", "" +}; + /* ** Growing our own isspace() routine this way is twice as fast as ** the library isspace() function, resulting in a 7% overall performance -** increase for the parser. (Ubuntu14.10 gcc 4.8.4 x64 with -Os). +** increase for the text-JSON parser. (Ubuntu14.10 gcc 4.8.4 x64 with -Os). */ static const char jsonIsSpace[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, @@ -201282,11 +203347,19 @@ static const char jsonIsSpace[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; -#define fast_isspace(x) (jsonIsSpace[(unsigned char)x]) +#define jsonIsspace(x) (jsonIsSpace[(unsigned char)x]) /* -** Characters that are special to JSON. Control charaters, -** '"' and '\\'. +** The set of all space characters recognized by jsonIsspace(). +** Useful as the second argument to strspn(). +*/ +static const char jsonSpaces[] = "\011\012\015\040"; + +/* +** Characters that are special to JSON. Control characters, +** '"' and '\\' and '\''. Actually, '\'' is not special to +** canonical JSON, but it is special in JSON-5, so we include +** it in the set of special characters. */ static const char jsonIsOk[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -201308,22 +203381,49 @@ static const char jsonIsOk[256] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; - -#if !defined(SQLITE_DEBUG) && !defined(SQLITE_COVERAGE_TEST) -# define VVA(X) -#else -# define VVA(X) X -#endif - /* Objects */ +typedef struct JsonCache JsonCache; typedef struct JsonString JsonString; -typedef struct JsonNode JsonNode; typedef struct JsonParse JsonParse; -typedef struct JsonCleanup JsonCleanup; + +/* +** Magic number used for the JSON parse cache in sqlite3_get_auxdata() +*/ +#define JSON_CACHE_ID (-429938) /* Cache entry */ +#define JSON_CACHE_SIZE 4 /* Max number of cache entries */ + +/* +** jsonUnescapeOneChar() returns this invalid code point if it encounters +** a syntax error. +*/ +#define JSON_INVALID_CHAR 0x99999 + +/* A cache mapping JSON text into JSONB blobs. +** +** Each cache entry is a JsonParse object with the following restrictions: +** +** * The bReadOnly flag must be set +** +** * The aBlob[] array must be owned by the JsonParse object. In other +** words, nBlobAlloc must be non-zero. +** +** * eEdit and delta must be zero. +** +** * zJson must be an RCStr. In other words bJsonIsRCStr must be true. +*/ +struct JsonCache { + sqlite3 *db; /* Database connection */ + int nUsed; /* Number of active entries in the cache */ + JsonParse *a[JSON_CACHE_SIZE]; /* One line for each cache entry */ +}; /* An instance of this object represents a JSON string ** under construction. Really, this is a generic string accumulator ** that can be and is used to create strings other than JSON. +** +** If the generated string is longer than will fit into the zSpace[] buffer, +** then it will be an RCStr string. This aids with caching of large +** JSON strings. */ struct JsonString { sqlite3_context *pCtx; /* Function context - put error messages here */ @@ -201331,121 +203431,75 @@ struct JsonString { u64 nAlloc; /* Bytes of storage available in zBuf[] */ u64 nUsed; /* Bytes of zBuf[] currently used */ u8 bStatic; /* True if zBuf is static space */ - u8 bErr; /* True if an error has been encountered */ + u8 eErr; /* True if an error has been encountered */ char zSpace[100]; /* Initial static space */ }; -/* A deferred cleanup task. A list of JsonCleanup objects might be -** run when the JsonParse object is destroyed. -*/ -struct JsonCleanup { - JsonCleanup *pJCNext; /* Next in a list */ - void (*xOp)(void*); /* Routine to run */ - void *pArg; /* Argument to xOp() */ -}; +/* Allowed values for JsonString.eErr */ +#define JSTRING_OOM 0x01 /* Out of memory */ +#define JSTRING_MALFORMED 0x02 /* Malformed JSONB */ +#define JSTRING_ERR 0x04 /* Error already sent to sqlite3_result */ -/* JSON type values +/* The "subtype" set for text JSON values passed through using +** sqlite3_result_subtype() and sqlite3_value_subtype(). */ -#define JSON_SUBST 0 /* Special edit node. Uses u.iPrev */ -#define JSON_NULL 1 -#define JSON_TRUE 2 -#define JSON_FALSE 3 -#define JSON_INT 4 -#define JSON_REAL 5 -#define JSON_STRING 6 -#define JSON_ARRAY 7 -#define JSON_OBJECT 8 - -/* The "subtype" set for JSON values */ #define JSON_SUBTYPE 74 /* Ascii for "J" */ /* -** Names of the various JSON types: +** Bit values for the flags passed into various SQL function implementations +** via the sqlite3_user_data() value. */ -static const char * const jsonType[] = { - "subst", - "null", "true", "false", "integer", "real", "text", "array", "object" -}; - -/* Bit values for the JsonNode.jnFlag field -*/ -#define JNODE_RAW 0x01 /* Content is raw, not JSON encoded */ -#define JNODE_ESCAPE 0x02 /* Content is text with \ escapes */ -#define JNODE_REMOVE 0x04 /* Do not output */ -#define JNODE_REPLACE 0x08 /* Target of a JSON_SUBST node */ -#define JNODE_APPEND 0x10 /* More ARRAY/OBJECT entries at u.iAppend */ -#define JNODE_LABEL 0x20 /* Is a label of an object */ -#define JNODE_JSON5 0x40 /* Node contains JSON5 enhancements */ +#define JSON_JSON 0x01 /* Result is always JSON */ +#define JSON_SQL 0x02 /* Result is always SQL */ +#define JSON_ABPATH 0x03 /* Allow abbreviated JSON path specs */ +#define JSON_ISSET 0x04 /* json_set(), not json_insert() */ +#define JSON_BLOB 0x08 /* Use the BLOB output format */ -/* A single node of parsed JSON. An array of these nodes describes -** a parse of JSON + edits. +/* A parsed JSON value. Lifecycle: ** -** Use the json_parse() SQL function (available when compiled with -** -DSQLITE_DEBUG) to see a dump of complete JsonParse objects, including -** a complete listing and decoding of the array of JsonNodes. -*/ -struct JsonNode { - u8 eType; /* One of the JSON_ type values */ - u8 jnFlags; /* JNODE flags */ - u8 eU; /* Which union element to use */ - u32 n; /* Bytes of content for INT, REAL or STRING - ** Number of sub-nodes for ARRAY and OBJECT - ** Node that SUBST applies to */ - union { - const char *zJContent; /* 1: Content for INT, REAL, and STRING */ - u32 iAppend; /* 2: More terms for ARRAY and OBJECT */ - u32 iKey; /* 3: Key for ARRAY objects in json_tree() */ - u32 iPrev; /* 4: Previous SUBST node, or 0 */ - } u; -}; - - -/* A parsed and possibly edited JSON string. Lifecycle: +** 1. JSON comes in and is parsed into a JSONB value in aBlob. The +** original text is stored in zJson. This step is skipped if the +** input is JSONB instead of text JSON. ** -** 1. JSON comes in and is parsed into an array aNode[]. The original -** JSON text is stored in zJson. +** 2. The aBlob[] array is searched using the JSON path notation, if needed. ** -** 2. Zero or more changes are made (via json_remove() or json_replace() -** or similar) to the aNode[] array. +** 3. Zero or more changes are made to aBlob[] (via json_remove() or +** json_replace() or json_patch() or similar). ** -** 3. A new, edited and mimified JSON string is generated from aNode -** and stored in zAlt. The JsonParse object always owns zAlt. -** -** Step 1 always happens. Step 2 and 3 may or may not happen, depending -** on the operation. -** -** aNode[].u.zJContent entries typically point into zJson. Hence zJson -** must remain valid for the lifespan of the parse. For edits, -** aNode[].u.zJContent might point to malloced space other than zJson. -** Entries in pClup are responsible for freeing that extra malloced space. -** -** When walking the parse tree in aNode[], edits are ignored if useMod is -** false. +** 4. New JSON text is generated from the aBlob[] for output. This step +** is skipped if the function is one of the jsonb_* functions that +** returns JSONB instead of text JSON. */ struct JsonParse { - u32 nNode; /* Number of slots of aNode[] used */ - u32 nAlloc; /* Number of slots of aNode[] allocated */ - JsonNode *aNode; /* Array of nodes containing the parse */ - char *zJson; /* Original JSON string (before edits) */ - char *zAlt; /* Revised and/or mimified JSON */ - u32 *aUp; /* Index of parent of each node */ - JsonCleanup *pClup;/* Cleanup operations prior to freeing this object */ + u8 *aBlob; /* JSONB representation of JSON value */ + u32 nBlob; /* Bytes of aBlob[] actually used */ + u32 nBlobAlloc; /* Bytes allocated to aBlob[]. 0 if aBlob is external */ + char *zJson; /* Json text used for parsing */ + sqlite3 *db; /* The database connection to which this object belongs */ + int nJson; /* Length of the zJson string in bytes */ + u32 nJPRef; /* Number of references to this object */ + u32 iErr; /* Error location in zJson[] */ u16 iDepth; /* Nesting depth */ u8 nErr; /* Number of errors seen */ u8 oom; /* Set to true if out of memory */ u8 bJsonIsRCStr; /* True if zJson is an RCStr */ u8 hasNonstd; /* True if input uses non-standard features like JSON5 */ - u8 useMod; /* Actually use the edits contain inside aNode */ - u8 hasMod; /* aNode contains edits from the original zJson */ - u32 nJPRef; /* Number of references to this object */ - int nJson; /* Length of the zJson string in bytes */ - int nAlt; /* Length of alternative JSON string zAlt, in bytes */ - u32 iErr; /* Error location in zJson[] */ - u32 iSubst; /* Last JSON_SUBST entry in aNode[] */ - u32 iHold; /* Age of this entry in the cache for LRU replacement */ + u8 bReadOnly; /* Do not modify. */ + /* Search and edit information. See jsonLookupStep() */ + u8 eEdit; /* Edit operation to apply */ + int delta; /* Size change due to the edit */ + u32 nIns; /* Number of bytes to insert */ + u32 iLabel; /* Location of label if search landed on an object value */ + u8 *aIns; /* Content to be inserted */ }; +/* Allowed values for JsonParse.eEdit */ +#define JEDIT_DEL 1 /* Delete if exists */ +#define JEDIT_REPL 2 /* Overwrite if exists */ +#define JEDIT_INS 3 /* Insert if not exists */ +#define JEDIT_SET 4 /* Insert or overwrite */ + /* ** Maximum nesting depth of JSON for this implementation. ** @@ -201453,15 +203507,151 @@ struct JsonParse { ** descent parser. A depth of 1000 is far deeper than any sane JSON ** should go. Historical note: This limit was 2000 prior to version 3.42.0 */ -#define JSON_MAX_DEPTH 1000 +#ifndef SQLITE_JSON_MAX_DEPTH +# define JSON_MAX_DEPTH 1000 +#else +# define JSON_MAX_DEPTH SQLITE_JSON_MAX_DEPTH +#endif + +/* +** Allowed values for the flgs argument to jsonParseFuncArg(); +*/ +#define JSON_EDITABLE 0x01 /* Generate a writable JsonParse object */ +#define JSON_KEEPERROR 0x02 /* Return non-NULL even if there is an error */ + +/************************************************************************** +** Forward references +**************************************************************************/ +static void jsonReturnStringAsBlob(JsonString*); +static int jsonFuncArgMightBeBinary(sqlite3_value *pJson); +static u32 jsonTranslateBlobToText(const JsonParse*,u32,JsonString*); +static void jsonReturnParse(sqlite3_context*,JsonParse*); +static JsonParse *jsonParseFuncArg(sqlite3_context*,sqlite3_value*,u32); +static void jsonParseFree(JsonParse*); +static u32 jsonbPayloadSize(const JsonParse*, u32, u32*); +static u32 jsonUnescapeOneChar(const char*, u32, u32*); + +/************************************************************************** +** Utility routines for dealing with JsonCache objects +**************************************************************************/ + +/* +** Free a JsonCache object. +*/ +static void jsonCacheDelete(JsonCache *p){ + int i; + for(i=0; inUsed; i++){ + jsonParseFree(p->a[i]); + } + sqlite3DbFree(p->db, p); +} +static void jsonCacheDeleteGeneric(void *p){ + jsonCacheDelete((JsonCache*)p); +} + +/* +** Insert a new entry into the cache. If the cache is full, expel +** the least recently used entry. Return SQLITE_OK on success or a +** result code otherwise. +** +** Cache entries are stored in age order, oldest first. +*/ +static int jsonCacheInsert( + sqlite3_context *ctx, /* The SQL statement context holding the cache */ + JsonParse *pParse /* The parse object to be added to the cache */ +){ + JsonCache *p; + + assert( pParse->zJson!=0 ); + assert( pParse->bJsonIsRCStr ); + assert( pParse->delta==0 ); + p = sqlite3_get_auxdata(ctx, JSON_CACHE_ID); + if( p==0 ){ + sqlite3 *db = sqlite3_context_db_handle(ctx); + p = sqlite3DbMallocZero(db, sizeof(*p)); + if( p==0 ) return SQLITE_NOMEM; + p->db = db; + sqlite3_set_auxdata(ctx, JSON_CACHE_ID, p, jsonCacheDeleteGeneric); + p = sqlite3_get_auxdata(ctx, JSON_CACHE_ID); + if( p==0 ) return SQLITE_NOMEM; + } + if( p->nUsed >= JSON_CACHE_SIZE ){ + jsonParseFree(p->a[0]); + memmove(p->a, &p->a[1], (JSON_CACHE_SIZE-1)*sizeof(p->a[0])); + p->nUsed = JSON_CACHE_SIZE-1; + } + assert( pParse->nBlobAlloc>0 ); + pParse->eEdit = 0; + pParse->nJPRef++; + pParse->bReadOnly = 1; + p->a[p->nUsed] = pParse; + p->nUsed++; + return SQLITE_OK; +} + +/* +** Search for a cached translation the json text supplied by pArg. Return +** the JsonParse object if found. Return NULL if not found. +** +** When a match if found, the matching entry is moved to become the +** most-recently used entry if it isn't so already. +** +** The JsonParse object returned still belongs to the Cache and might +** be deleted at any moment. If the caller whants the JsonParse to +** linger, it needs to increment the nPJRef reference counter. +*/ +static JsonParse *jsonCacheSearch( + sqlite3_context *ctx, /* The SQL statement context holding the cache */ + sqlite3_value *pArg /* Function argument containing SQL text */ +){ + JsonCache *p; + int i; + const char *zJson; + int nJson; + + if( sqlite3_value_type(pArg)!=SQLITE_TEXT ){ + return 0; + } + zJson = (const char*)sqlite3_value_text(pArg); + if( zJson==0 ) return 0; + nJson = sqlite3_value_bytes(pArg); + + p = sqlite3_get_auxdata(ctx, JSON_CACHE_ID); + if( p==0 ){ + return 0; + } + for(i=0; inUsed; i++){ + if( p->a[i]->zJson==zJson ) break; + } + if( i>=p->nUsed ){ + for(i=0; inUsed; i++){ + if( p->a[i]->nJson!=nJson ) continue; + if( memcmp(p->a[i]->zJson, zJson, nJson)==0 ) break; + } + } + if( inUsed ){ + if( inUsed-1 ){ + /* Make the matching entry the most recently used entry */ + JsonParse *tmp = p->a[i]; + memmove(&p->a[i], &p->a[i+1], (p->nUsed-i-1)*sizeof(tmp)); + p->a[p->nUsed-1] = tmp; + i = p->nUsed - 1; + } + assert( p->a[i]->delta==0 ); + return p->a[i]; + }else{ + return 0; + } +} /************************************************************************** ** Utility routines for dealing with JsonString objects **************************************************************************/ -/* Set the JsonString object to an empty string +/* Turn uninitialized bulk memory into a valid JsonString object +** holding a zero-length string. */ -static void jsonZero(JsonString *p){ +static void jsonStringZero(JsonString *p){ p->zBuf = p->zSpace; p->nAlloc = sizeof(p->zSpace); p->nUsed = 0; @@ -201470,39 +203660,39 @@ static void jsonZero(JsonString *p){ /* Initialize the JsonString object */ -static void jsonInit(JsonString *p, sqlite3_context *pCtx){ +static void jsonStringInit(JsonString *p, sqlite3_context *pCtx){ p->pCtx = pCtx; - p->bErr = 0; - jsonZero(p); + p->eErr = 0; + jsonStringZero(p); } /* Free all allocated memory and reset the JsonString object back to its ** initial state. */ -static void jsonReset(JsonString *p){ +static void jsonStringReset(JsonString *p){ if( !p->bStatic ) sqlite3RCStrUnref(p->zBuf); - jsonZero(p); + jsonStringZero(p); } /* Report an out-of-memory (OOM) condition */ -static void jsonOom(JsonString *p){ - p->bErr = 1; - sqlite3_result_error_nomem(p->pCtx); - jsonReset(p); +static void jsonStringOom(JsonString *p){ + p->eErr |= JSTRING_OOM; + if( p->pCtx ) sqlite3_result_error_nomem(p->pCtx); + jsonStringReset(p); } /* Enlarge pJson->zBuf so that it can hold at least N more bytes. ** Return zero on success. Return non-zero on an OOM error */ -static int jsonGrow(JsonString *p, u32 N){ +static int jsonStringGrow(JsonString *p, u32 N){ u64 nTotal = NnAlloc ? p->nAlloc*2 : p->nAlloc+N+10; char *zNew; if( p->bStatic ){ - if( p->bErr ) return 1; + if( p->eErr ) return 1; zNew = sqlite3RCStrNew(nTotal); if( zNew==0 ){ - jsonOom(p); + jsonStringOom(p); return SQLITE_NOMEM; } memcpy(zNew, p->zBuf, (size_t)p->nUsed); @@ -201511,8 +203701,8 @@ static int jsonGrow(JsonString *p, u32 N){ }else{ p->zBuf = sqlite3RCStrResize(p->zBuf, nTotal); if( p->zBuf==0 ){ - p->bErr = 1; - jsonZero(p); + p->eErr |= JSTRING_OOM; + jsonStringZero(p); return SQLITE_NOMEM; } } @@ -201522,20 +203712,20 @@ static int jsonGrow(JsonString *p, u32 N){ /* Append N bytes from zIn onto the end of the JsonString string. */ -static SQLITE_NOINLINE void jsonAppendExpand( +static SQLITE_NOINLINE void jsonStringExpandAndAppend( JsonString *p, const char *zIn, u32 N ){ assert( N>0 ); - if( jsonGrow(p,N) ) return; + if( jsonStringGrow(p,N) ) return; memcpy(p->zBuf+p->nUsed, zIn, N); p->nUsed += N; } static void jsonAppendRaw(JsonString *p, const char *zIn, u32 N){ if( N==0 ) return; if( N+p->nUsed >= p->nAlloc ){ - jsonAppendExpand(p,zIn,N); + jsonStringExpandAndAppend(p,zIn,N); }else{ memcpy(p->zBuf+p->nUsed, zIn, N); p->nUsed += N; @@ -201544,7 +203734,7 @@ static void jsonAppendRaw(JsonString *p, const char *zIn, u32 N){ static void jsonAppendRawNZ(JsonString *p, const char *zIn, u32 N){ assert( N>0 ); if( N+p->nUsed >= p->nAlloc ){ - jsonAppendExpand(p,zIn,N); + jsonStringExpandAndAppend(p,zIn,N); }else{ memcpy(p->zBuf+p->nUsed, zIn, N); p->nUsed += N; @@ -201556,7 +203746,7 @@ static void jsonAppendRawNZ(JsonString *p, const char *zIn, u32 N){ */ static void jsonPrintf(int N, JsonString *p, const char *zFormat, ...){ va_list ap; - if( (p->nUsed + N >= p->nAlloc) && jsonGrow(p, N) ) return; + if( (p->nUsed + N >= p->nAlloc) && jsonStringGrow(p, N) ) return; va_start(ap, zFormat); sqlite3_vsnprintf(N, p->zBuf+p->nUsed, zFormat, ap); va_end(ap); @@ -201566,7 +203756,7 @@ static void jsonPrintf(int N, JsonString *p, const char *zFormat, ...){ /* Append a single character */ static SQLITE_NOINLINE void jsonAppendCharExpand(JsonString *p, char c){ - if( jsonGrow(p,1) ) return; + if( jsonStringGrow(p,1) ) return; p->zBuf[p->nUsed++] = c; } static void jsonAppendChar(JsonString *p, char c){ @@ -201577,24 +203767,27 @@ static void jsonAppendChar(JsonString *p, char c){ } } -/* Try to force the string to be a zero-terminated RCStr string. +/* Remove a single character from the end of the string +*/ +static void jsonStringTrimOneChar(JsonString *p){ + if( p->eErr==0 ){ + assert( p->nUsed>0 ); + p->nUsed--; + } +} + + +/* Make sure there is a zero terminator on p->zBuf[] ** ** Return true on success. Return false if an OOM prevents this ** from happening. */ -static int jsonForceRCStr(JsonString *p){ +static int jsonStringTerminate(JsonString *p){ jsonAppendChar(p, 0); - if( p->bErr ) return 0; - p->nUsed--; - if( p->bStatic==0 ) return 1; - p->nAlloc = 0; - p->nUsed++; - jsonGrow(p, p->nUsed); - p->nUsed--; - return p->bStatic==0; + jsonStringTrimOneChar(p); + return p->eErr==0; } - /* Append a comma separator to the output buffer, if the previous ** character is not '[' or '{'. */ @@ -201607,21 +203800,66 @@ static void jsonAppendSeparator(JsonString *p){ } /* Append the N-byte string in zIn to the end of the JsonString string -** under construction. Enclose the string in "..." and escape -** any double-quotes or backslash characters contained within the +** under construction. Enclose the string in double-quotes ("...") and +** escape any double-quotes or backslash characters contained within the ** string. +** +** This routine is a high-runner. There is a measurable performance +** increase associated with unwinding the jsonIsOk[] loop. */ static void jsonAppendString(JsonString *p, const char *zIn, u32 N){ - u32 i; - if( zIn==0 || ((N+p->nUsed+2 >= p->nAlloc) && jsonGrow(p,N+2)!=0) ) return; + u32 k; + u8 c; + const u8 *z = (const u8*)zIn; + if( z==0 ) return; + if( (N+p->nUsed+2 >= p->nAlloc) && jsonStringGrow(p,N+2)!=0 ) return; p->zBuf[p->nUsed++] = '"'; - for(i=0; izBuf[p->nUsed++] = c; - }else if( c=='"' || c=='\\' ){ + while( 1 /*exit-by-break*/ ){ + k = 0; + /* The following while() is the 4-way unwound equivalent of + ** + ** while( k=N ){ + while( k=N ){ + if( k>0 ){ + memcpy(&p->zBuf[p->nUsed], z, k); + p->nUsed += k; + } + break; + } + if( k>0 ){ + memcpy(&p->zBuf[p->nUsed], z, k); + p->nUsed += k; + z += k; + N -= k; + } + c = z[0]; + if( c=='"' || c=='\\' ){ json_simple_escape: - if( (p->nUsed+N+3-i > p->nAlloc) && jsonGrow(p,N+3-i)!=0 ) return; + if( (p->nUsed+N+3 > p->nAlloc) && jsonStringGrow(p,N+3)!=0 ) return; p->zBuf[p->nUsed++] = '\\'; p->zBuf[p->nUsed++] = c; }else if( c=='\'' ){ @@ -201642,7 +203880,7 @@ static void jsonAppendString(JsonString *p, const char *zIn, u32 N){ c = aSpecial[c]; goto json_simple_escape; } - if( (p->nUsed+N+7+i > p->nAlloc) && jsonGrow(p,N+7-i)!=0 ) return; + if( (p->nUsed+N+7 > p->nAlloc) && jsonStringGrow(p,N+7)!=0 ) return; p->zBuf[p->nUsed++] = '\\'; p->zBuf[p->nUsed++] = 'u'; p->zBuf[p->nUsed++] = '0'; @@ -201650,140 +203888,18 @@ static void jsonAppendString(JsonString *p, const char *zIn, u32 N){ p->zBuf[p->nUsed++] = "0123456789abcdef"[c>>4]; p->zBuf[p->nUsed++] = "0123456789abcdef"[c&0xf]; } + z++; + N--; } p->zBuf[p->nUsed++] = '"'; assert( p->nUsednAlloc ); } /* -** The zIn[0..N] string is a JSON5 string literal. Append to p a translation -** of the string literal that standard JSON and that omits all JSON5 -** features. +** Append an sqlite3_value (such as a function parameter) to the JSON +** string under construction in p. */ -static void jsonAppendNormalizedString(JsonString *p, const char *zIn, u32 N){ - u32 i; - jsonAppendChar(p, '"'); - zIn++; - N -= 2; - while( N>0 ){ - for(i=0; i0 ){ - jsonAppendRawNZ(p, zIn, i); - zIn += i; - N -= i; - if( N==0 ) break; - } - assert( zIn[0]=='\\' ); - switch( (u8)zIn[1] ){ - case '\'': - jsonAppendChar(p, '\''); - break; - case 'v': - jsonAppendRawNZ(p, "\\u0009", 6); - break; - case 'x': - jsonAppendRawNZ(p, "\\u00", 4); - jsonAppendRawNZ(p, &zIn[2], 2); - zIn += 2; - N -= 2; - break; - case '0': - jsonAppendRawNZ(p, "\\u0000", 6); - break; - case '\r': - if( zIn[2]=='\n' ){ - zIn++; - N--; - } - break; - case '\n': - break; - case 0xe2: - assert( N>=4 ); - assert( 0x80==(u8)zIn[2] ); - assert( 0xa8==(u8)zIn[3] || 0xa9==(u8)zIn[3] ); - zIn += 2; - N -= 2; - break; - default: - jsonAppendRawNZ(p, zIn, 2); - break; - } - zIn += 2; - N -= 2; - } - jsonAppendChar(p, '"'); -} - -/* -** The zIn[0..N] string is a JSON5 integer literal. Append to p a translation -** of the string literal that standard JSON and that omits all JSON5 -** features. -*/ -static void jsonAppendNormalizedInt(JsonString *p, const char *zIn, u32 N){ - if( zIn[0]=='+' ){ - zIn++; - N--; - }else if( zIn[0]=='-' ){ - jsonAppendChar(p, '-'); - zIn++; - N--; - } - if( zIn[0]=='0' && (zIn[1]=='x' || zIn[1]=='X') ){ - sqlite3_int64 i = 0; - int rc = sqlite3DecOrHexToI64(zIn, &i); - if( rc<=1 ){ - jsonPrintf(100,p,"%lld",i); - }else{ - assert( rc==2 ); - jsonAppendRawNZ(p, "9.0e999", 7); - } - return; - } - assert( N>0 ); - jsonAppendRawNZ(p, zIn, N); -} - -/* -** The zIn[0..N] string is a JSON5 real literal. Append to p a translation -** of the string literal that standard JSON and that omits all JSON5 -** features. -*/ -static void jsonAppendNormalizedReal(JsonString *p, const char *zIn, u32 N){ - u32 i; - if( zIn[0]=='+' ){ - zIn++; - N--; - }else if( zIn[0]=='-' ){ - jsonAppendChar(p, '-'); - zIn++; - N--; - } - if( zIn[0]=='.' ){ - jsonAppendChar(p, '0'); - } - for(i=0; i0 ){ - jsonAppendRawNZ(p, zIn, N); - } -} - - - -/* -** Append a function parameter value to the JSON string under -** construction. -*/ -static void jsonAppendValue( +static void jsonAppendSqlValue( JsonString *p, /* Append to this JSON string */ sqlite3_value *pValue /* Value to append */ ){ @@ -201813,290 +203929,127 @@ static void jsonAppendValue( break; } default: { - if( p->bErr==0 ){ + if( jsonFuncArgMightBeBinary(pValue) ){ + JsonParse px; + memset(&px, 0, sizeof(px)); + px.aBlob = (u8*)sqlite3_value_blob(pValue); + px.nBlob = sqlite3_value_bytes(pValue); + jsonTranslateBlobToText(&px, 0, p); + }else if( p->eErr==0 ){ sqlite3_result_error(p->pCtx, "JSON cannot hold BLOB values", -1); - p->bErr = 2; - jsonReset(p); + p->eErr = JSTRING_ERR; + jsonStringReset(p); } break; } } } - -/* Make the JSON in p the result of the SQL function. +/* Make the text in p (which is probably a generated JSON text string) +** the result of the SQL function. ** -** The JSON string is reset. +** The JsonString is reset. +** +** If pParse and ctx are both non-NULL, then the SQL string in p is +** loaded into the zJson field of the pParse object as a RCStr and the +** pParse is added to the cache. */ -static void jsonResult(JsonString *p){ - if( p->bErr==0 ){ - if( p->bStatic ){ +static void jsonReturnString( + JsonString *p, /* String to return */ + JsonParse *pParse, /* JSONB source or NULL */ + sqlite3_context *ctx /* Where to cache */ +){ + assert( (pParse!=0)==(ctx!=0) ); + assert( ctx==0 || ctx==p->pCtx ); + if( p->eErr==0 ){ + int flags = SQLITE_PTR_TO_INT(sqlite3_user_data(p->pCtx)); + if( flags & JSON_BLOB ){ + jsonReturnStringAsBlob(p); + }else if( p->bStatic ){ sqlite3_result_text64(p->pCtx, p->zBuf, p->nUsed, SQLITE_TRANSIENT, SQLITE_UTF8); - }else if( jsonForceRCStr(p) ){ - sqlite3RCStrRef(p->zBuf); - sqlite3_result_text64(p->pCtx, p->zBuf, p->nUsed, - (void(*)(void*))sqlite3RCStrUnref, + }else if( jsonStringTerminate(p) ){ + if( pParse && pParse->bJsonIsRCStr==0 && pParse->nBlobAlloc>0 ){ + int rc; + pParse->zJson = sqlite3RCStrRef(p->zBuf); + pParse->nJson = p->nUsed; + pParse->bJsonIsRCStr = 1; + rc = jsonCacheInsert(ctx, pParse); + if( rc==SQLITE_NOMEM ){ + sqlite3_result_error_nomem(ctx); + jsonStringReset(p); + return; + } + } + sqlite3_result_text64(p->pCtx, sqlite3RCStrRef(p->zBuf), p->nUsed, + sqlite3RCStrUnref, SQLITE_UTF8); + }else{ + sqlite3_result_error_nomem(p->pCtx); } - } - if( p->bErr==1 ){ + }else if( p->eErr & JSTRING_OOM ){ sqlite3_result_error_nomem(p->pCtx); + }else if( p->eErr & JSTRING_MALFORMED ){ + sqlite3_result_error(p->pCtx, "malformed JSON", -1); } - jsonReset(p); + jsonStringReset(p); } /************************************************************************** -** Utility routines for dealing with JsonNode and JsonParse objects +** Utility routines for dealing with JsonParse objects **************************************************************************/ -/* -** Return the number of consecutive JsonNode slots need to represent -** the parsed JSON at pNode. The minimum answer is 1. For ARRAY and -** OBJECT types, the number might be larger. -** -** Appended elements are not counted. The value returned is the number -** by which the JsonNode counter should increment in order to go to the -** next peer value. -*/ -static u32 jsonNodeSize(JsonNode *pNode){ - return pNode->eType>=JSON_ARRAY ? pNode->n+1 : 1; -} - /* ** Reclaim all memory allocated by a JsonParse object. But do not ** delete the JsonParse object itself. */ static void jsonParseReset(JsonParse *pParse){ - while( pParse->pClup ){ - JsonCleanup *pTask = pParse->pClup; - pParse->pClup = pTask->pJCNext; - pTask->xOp(pTask->pArg); - sqlite3_free(pTask); - } assert( pParse->nJPRef<=1 ); - if( pParse->aNode ){ - sqlite3_free(pParse->aNode); - pParse->aNode = 0; - } - pParse->nNode = 0; - pParse->nAlloc = 0; - if( pParse->aUp ){ - sqlite3_free(pParse->aUp); - pParse->aUp = 0; - } if( pParse->bJsonIsRCStr ){ sqlite3RCStrUnref(pParse->zJson); pParse->zJson = 0; + pParse->nJson = 0; pParse->bJsonIsRCStr = 0; } - if( pParse->zAlt ){ - sqlite3RCStrUnref(pParse->zAlt); - pParse->zAlt = 0; + if( pParse->nBlobAlloc ){ + sqlite3DbFree(pParse->db, pParse->aBlob); + pParse->aBlob = 0; + pParse->nBlob = 0; + pParse->nBlobAlloc = 0; } } /* -** Free a JsonParse object that was obtained from sqlite3_malloc(). -** -** Note that destroying JsonParse might call sqlite3RCStrUnref() to -** destroy the zJson value. The RCStr object might recursively invoke -** JsonParse to destroy this pParse object again. Take care to ensure -** that this recursive destructor sequence terminates harmlessly. +** Decrement the reference count on the JsonParse object. When the +** count reaches zero, free the object. */ static void jsonParseFree(JsonParse *pParse){ - if( pParse->nJPRef>1 ){ - pParse->nJPRef--; - }else{ - jsonParseReset(pParse); - sqlite3_free(pParse); - } -} - -/* -** Add a cleanup task to the JsonParse object. -** -** If an OOM occurs, the cleanup operation happens immediately -** and this function returns SQLITE_NOMEM. -*/ -static int jsonParseAddCleanup( - JsonParse *pParse, /* Add the cleanup task to this parser */ - void(*xOp)(void*), /* The cleanup task */ - void *pArg /* Argument to the cleanup */ -){ - JsonCleanup *pTask = sqlite3_malloc64( sizeof(*pTask) ); - if( pTask==0 ){ - pParse->oom = 1; - xOp(pArg); - return SQLITE_ERROR; - } - pTask->pJCNext = pParse->pClup; - pParse->pClup = pTask; - pTask->xOp = xOp; - pTask->pArg = pArg; - return SQLITE_OK; -} - -/* -** Convert the JsonNode pNode into a pure JSON string and -** append to pOut. Subsubstructure is also included. Return -** the number of JsonNode objects that are encoded. -*/ -static void jsonRenderNode( - JsonParse *pParse, /* the complete parse of the JSON */ - JsonNode *pNode, /* The node to render */ - JsonString *pOut /* Write JSON here */ -){ - assert( pNode!=0 ); - while( (pNode->jnFlags & JNODE_REPLACE)!=0 && pParse->useMod ){ - u32 idx = (u32)(pNode - pParse->aNode); - u32 i = pParse->iSubst; - while( 1 /*exit-by-break*/ ){ - assert( inNode ); - assert( pParse->aNode[i].eType==JSON_SUBST ); - assert( pParse->aNode[i].eU==4 ); - assert( pParse->aNode[i].u.iPrevaNode[i].n==idx ){ - pNode = &pParse->aNode[i+1]; - break; - } - i = pParse->aNode[i].u.iPrev; - } - } - switch( pNode->eType ){ - default: { - assert( pNode->eType==JSON_NULL ); - jsonAppendRawNZ(pOut, "null", 4); - break; - } - case JSON_TRUE: { - jsonAppendRawNZ(pOut, "true", 4); - break; - } - case JSON_FALSE: { - jsonAppendRawNZ(pOut, "false", 5); - break; - } - case JSON_STRING: { - assert( pNode->eU==1 ); - if( pNode->jnFlags & JNODE_RAW ){ - if( pNode->jnFlags & JNODE_LABEL ){ - jsonAppendChar(pOut, '"'); - jsonAppendRaw(pOut, pNode->u.zJContent, pNode->n); - jsonAppendChar(pOut, '"'); - }else{ - jsonAppendString(pOut, pNode->u.zJContent, pNode->n); - } - }else if( pNode->jnFlags & JNODE_JSON5 ){ - jsonAppendNormalizedString(pOut, pNode->u.zJContent, pNode->n); - }else{ - assert( pNode->n>0 ); - jsonAppendRawNZ(pOut, pNode->u.zJContent, pNode->n); - } - break; - } - case JSON_REAL: { - assert( pNode->eU==1 ); - if( pNode->jnFlags & JNODE_JSON5 ){ - jsonAppendNormalizedReal(pOut, pNode->u.zJContent, pNode->n); - }else{ - assert( pNode->n>0 ); - jsonAppendRawNZ(pOut, pNode->u.zJContent, pNode->n); - } - break; - } - case JSON_INT: { - assert( pNode->eU==1 ); - if( pNode->jnFlags & JNODE_JSON5 ){ - jsonAppendNormalizedInt(pOut, pNode->u.zJContent, pNode->n); - }else{ - assert( pNode->n>0 ); - jsonAppendRawNZ(pOut, pNode->u.zJContent, pNode->n); - } - break; - } - case JSON_ARRAY: { - u32 j = 1; - jsonAppendChar(pOut, '['); - for(;;){ - while( j<=pNode->n ){ - if( (pNode[j].jnFlags & JNODE_REMOVE)==0 || pParse->useMod==0 ){ - jsonAppendSeparator(pOut); - jsonRenderNode(pParse, &pNode[j], pOut); - } - j += jsonNodeSize(&pNode[j]); - } - if( (pNode->jnFlags & JNODE_APPEND)==0 ) break; - if( pParse->useMod==0 ) break; - assert( pNode->eU==2 ); - pNode = &pParse->aNode[pNode->u.iAppend]; - j = 1; - } - jsonAppendChar(pOut, ']'); - break; - } - case JSON_OBJECT: { - u32 j = 1; - jsonAppendChar(pOut, '{'); - for(;;){ - while( j<=pNode->n ){ - if( (pNode[j+1].jnFlags & JNODE_REMOVE)==0 || pParse->useMod==0 ){ - jsonAppendSeparator(pOut); - jsonRenderNode(pParse, &pNode[j], pOut); - jsonAppendChar(pOut, ':'); - jsonRenderNode(pParse, &pNode[j+1], pOut); - } - j += 1 + jsonNodeSize(&pNode[j+1]); - } - if( (pNode->jnFlags & JNODE_APPEND)==0 ) break; - if( pParse->useMod==0 ) break; - assert( pNode->eU==2 ); - pNode = &pParse->aNode[pNode->u.iAppend]; - j = 1; - } - jsonAppendChar(pOut, '}'); - break; + if( pParse ){ + if( pParse->nJPRef>1 ){ + pParse->nJPRef--; + }else{ + jsonParseReset(pParse); + sqlite3DbFree(pParse->db, pParse); } } } -/* -** Return a JsonNode and all its descendants as a JSON string. -*/ -static void jsonReturnJson( - JsonParse *pParse, /* The complete JSON */ - JsonNode *pNode, /* Node to return */ - sqlite3_context *pCtx, /* Return value for this function */ - int bGenerateAlt /* Also store the rendered text in zAlt */ -){ - JsonString s; - if( pParse->oom ){ - sqlite3_result_error_nomem(pCtx); - return; - } - if( pParse->nErr==0 ){ - jsonInit(&s, pCtx); - jsonRenderNode(pParse, pNode, &s); - if( bGenerateAlt && pParse->zAlt==0 && jsonForceRCStr(&s) ){ - pParse->zAlt = sqlite3RCStrRef(s.zBuf); - pParse->nAlt = s.nUsed; - } - jsonResult(&s); - sqlite3_result_subtype(pCtx, JSON_SUBTYPE); - } -} +/************************************************************************** +** Utility routines for the JSON text parser +**************************************************************************/ /* ** Translate a single byte of Hex into an integer. -** This routine only works if h really is a valid hexadecimal -** character: 0..9a..fA..F +** This routine only gives a correct answer if h really is a valid hexadecimal +** character: 0..9a..fA..F. But unlike sqlite3HexToInt(), it does not +** assert() if the digit is not hex. */ static u8 jsonHexToInt(int h){ - assert( (h>='0' && h<='9') || (h>='a' && h<='f') || (h>='A' && h<='F') ); +#ifdef SQLITE_ASCII + h += 9*(1&(h>>6)); +#endif #ifdef SQLITE_EBCDIC h += 9*(1&~(h>>4)); -#else - h += 9*(1&(h>>6)); #endif return (u8)(h & 0xf); } @@ -202106,10 +204059,6 @@ static u8 jsonHexToInt(int h){ */ static u32 jsonHexToInt4(const char *z){ u32 v; - assert( sqlite3Isxdigit(z[0]) ); - assert( sqlite3Isxdigit(z[1]) ); - assert( sqlite3Isxdigit(z[2]) ); - assert( sqlite3Isxdigit(z[3]) ); v = (jsonHexToInt(z[0])<<12) + (jsonHexToInt(z[1])<<8) + (jsonHexToInt(z[2])<<4) @@ -202117,281 +204066,6 @@ static u32 jsonHexToInt4(const char *z){ return v; } -/* -** Make the JsonNode the return value of the function. -*/ -static void jsonReturn( - JsonParse *pParse, /* Complete JSON parse tree */ - JsonNode *pNode, /* Node to return */ - sqlite3_context *pCtx /* Return value for this function */ -){ - switch( pNode->eType ){ - default: { - assert( pNode->eType==JSON_NULL ); - sqlite3_result_null(pCtx); - break; - } - case JSON_TRUE: { - sqlite3_result_int(pCtx, 1); - break; - } - case JSON_FALSE: { - sqlite3_result_int(pCtx, 0); - break; - } - case JSON_INT: { - sqlite3_int64 i = 0; - int rc; - int bNeg = 0; - const char *z; - - assert( pNode->eU==1 ); - z = pNode->u.zJContent; - if( z[0]=='-' ){ z++; bNeg = 1; } - else if( z[0]=='+' ){ z++; } - rc = sqlite3DecOrHexToI64(z, &i); - if( rc<=1 ){ - sqlite3_result_int64(pCtx, bNeg ? -i : i); - }else if( rc==3 && bNeg ){ - sqlite3_result_int64(pCtx, SMALLEST_INT64); - }else{ - goto to_double; - } - break; - } - case JSON_REAL: { - double r; - const char *z; - assert( pNode->eU==1 ); - to_double: - z = pNode->u.zJContent; - sqlite3AtoF(z, &r, sqlite3Strlen30(z), SQLITE_UTF8); - sqlite3_result_double(pCtx, r); - break; - } - case JSON_STRING: { - if( pNode->jnFlags & JNODE_RAW ){ - assert( pNode->eU==1 ); - sqlite3_result_text(pCtx, pNode->u.zJContent, pNode->n, - SQLITE_TRANSIENT); - }else if( (pNode->jnFlags & JNODE_ESCAPE)==0 ){ - /* JSON formatted without any backslash-escapes */ - assert( pNode->eU==1 ); - sqlite3_result_text(pCtx, pNode->u.zJContent+1, pNode->n-2, - SQLITE_TRANSIENT); - }else{ - /* Translate JSON formatted string into raw text */ - u32 i; - u32 n = pNode->n; - const char *z; - char *zOut; - u32 j; - u32 nOut = n; - assert( pNode->eU==1 ); - z = pNode->u.zJContent; - zOut = sqlite3_malloc( nOut+1 ); - if( zOut==0 ){ - sqlite3_result_error_nomem(pCtx); - break; - } - for(i=1, j=0; i>6)); - zOut[j++] = 0x80 | (v&0x3f); - }else{ - u32 vlo; - if( (v&0xfc00)==0xd800 - && i>18); - zOut[j++] = 0x80 | ((v>>12)&0x3f); - zOut[j++] = 0x80 | ((v>>6)&0x3f); - zOut[j++] = 0x80 | (v&0x3f); - }else{ - zOut[j++] = 0xe0 | (v>>12); - zOut[j++] = 0x80 | ((v>>6)&0x3f); - zOut[j++] = 0x80 | (v&0x3f); - } - } - continue; - }else if( c=='b' ){ - c = '\b'; - }else if( c=='f' ){ - c = '\f'; - }else if( c=='n' ){ - c = '\n'; - }else if( c=='r' ){ - c = '\r'; - }else if( c=='t' ){ - c = '\t'; - }else if( c=='v' ){ - c = '\v'; - }else if( c=='\'' || c=='"' || c=='/' || c=='\\' ){ - /* pass through unchanged */ - }else if( c=='0' ){ - c = 0; - }else if( c=='x' ){ - c = (jsonHexToInt(z[i+1])<<4) | jsonHexToInt(z[i+2]); - i += 2; - }else if( c=='\r' && z[i+1]=='\n' ){ - i++; - continue; - }else if( 0xe2==(u8)c ){ - assert( 0x80==(u8)z[i+1] ); - assert( 0xa8==(u8)z[i+2] || 0xa9==(u8)z[i+2] ); - i += 2; - continue; - }else{ - continue; - } - } /* end if( c=='\\' ) */ - zOut[j++] = c; - } /* end for() */ - zOut[j] = 0; - sqlite3_result_text(pCtx, zOut, j, sqlite3_free); - } - break; - } - case JSON_ARRAY: - case JSON_OBJECT: { - jsonReturnJson(pParse, pNode, pCtx, 0); - break; - } - } -} - -/* Forward reference */ -static int jsonParseAddNode(JsonParse*,u32,u32,const char*); - -/* -** A macro to hint to the compiler that a function should not be -** inlined. -*/ -#if defined(__GNUC__) -# define JSON_NOINLINE __attribute__((noinline)) -#elif defined(_MSC_VER) && _MSC_VER>=1310 -# define JSON_NOINLINE __declspec(noinline) -#else -# define JSON_NOINLINE -#endif - - -/* -** Add a single node to pParse->aNode after first expanding the -** size of the aNode array. Return the index of the new node. -** -** If an OOM error occurs, set pParse->oom and return -1. -*/ -static JSON_NOINLINE int jsonParseAddNodeExpand( - JsonParse *pParse, /* Append the node to this object */ - u32 eType, /* Node type */ - u32 n, /* Content size or sub-node count */ - const char *zContent /* Content */ -){ - u32 nNew; - JsonNode *pNew; - assert( pParse->nNode>=pParse->nAlloc ); - if( pParse->oom ) return -1; - nNew = pParse->nAlloc*2 + 10; - pNew = sqlite3_realloc64(pParse->aNode, sizeof(JsonNode)*nNew); - if( pNew==0 ){ - pParse->oom = 1; - return -1; - } - pParse->nAlloc = sqlite3_msize(pNew)/sizeof(JsonNode); - pParse->aNode = pNew; - assert( pParse->nNodenAlloc ); - return jsonParseAddNode(pParse, eType, n, zContent); -} - -/* -** Create a new JsonNode instance based on the arguments and append that -** instance to the JsonParse. Return the index in pParse->aNode[] of the -** new node, or -1 if a memory allocation fails. -*/ -static int jsonParseAddNode( - JsonParse *pParse, /* Append the node to this object */ - u32 eType, /* Node type */ - u32 n, /* Content size or sub-node count */ - const char *zContent /* Content */ -){ - JsonNode *p; - assert( pParse->aNode!=0 || pParse->nNode>=pParse->nAlloc ); - if( pParse->nNode>=pParse->nAlloc ){ - return jsonParseAddNodeExpand(pParse, eType, n, zContent); - } - assert( pParse->aNode!=0 ); - p = &pParse->aNode[pParse->nNode]; - assert( p!=0 ); - p->eType = (u8)(eType & 0xff); - p->jnFlags = (u8)(eType >> 8); - VVA( p->eU = zContent ? 1 : 0 ); - p->n = n; - p->u.zJContent = zContent; - return pParse->nNode++; -} - -/* -** Add an array of new nodes to the current pParse->aNode array. -** Return the index of the first node added. -** -** If an OOM error occurs, set pParse->oom. -*/ -static void jsonParseAddNodeArray( - JsonParse *pParse, /* Append the node to this object */ - JsonNode *aNode, /* Array of nodes to add */ - u32 nNode /* Number of elements in aNew */ -){ - assert( aNode!=0 ); - assert( nNode>=1 ); - if( pParse->nNode + nNode > pParse->nAlloc ){ - u32 nNew = pParse->nNode + nNode; - JsonNode *aNew = sqlite3_realloc64(pParse->aNode, nNew*sizeof(JsonNode)); - if( aNew==0 ){ - pParse->oom = 1; - return; - } - pParse->nAlloc = sqlite3_msize(aNew)/sizeof(JsonNode); - pParse->aNode = aNew; - } - memcpy(&pParse->aNode[pParse->nNode], aNode, nNode*sizeof(JsonNode)); - pParse->nNode += nNode; -} - -/* -** Add a new JSON_SUBST node. The node immediately following -** this new node will be the substitute content for iNode. -*/ -static int jsonParseAddSubstNode( - JsonParse *pParse, /* Add the JSON_SUBST here */ - u32 iNode /* References this node */ -){ - int idx = jsonParseAddNode(pParse, JSON_SUBST, iNode, 0); - if( pParse->oom ) return -1; - pParse->aNode[iNode].jnFlags |= JNODE_REPLACE; - pParse->aNode[idx].eU = 4; - pParse->aNode[idx].u.iPrev = pParse->iSubst; - pParse->iSubst = idx; - pParse->hasMod = 1; - pParse->useMod = 1; - return idx; -} - /* ** Return true if z[] begins with 2 (or more) hexadecimal digits */ @@ -202545,63 +204219,500 @@ static const struct NanInfName { char *zMatch; char *zRepl; } aNanInfName[] = { - { 'i', 'I', 3, JSON_REAL, 7, "inf", "9.0e999" }, - { 'i', 'I', 8, JSON_REAL, 7, "infinity", "9.0e999" }, - { 'n', 'N', 3, JSON_NULL, 4, "NaN", "null" }, - { 'q', 'Q', 4, JSON_NULL, 4, "QNaN", "null" }, - { 's', 'S', 4, JSON_NULL, 4, "SNaN", "null" }, + { 'i', 'I', 3, JSONB_FLOAT, 7, "inf", "9.0e999" }, + { 'i', 'I', 8, JSONB_FLOAT, 7, "infinity", "9.0e999" }, + { 'n', 'N', 3, JSONB_NULL, 4, "NaN", "null" }, + { 'q', 'Q', 4, JSONB_NULL, 4, "QNaN", "null" }, + { 's', 'S', 4, JSONB_NULL, 4, "SNaN", "null" }, }; + /* -** Parse a single JSON value which begins at pParse->zJson[i]. Return the -** index of the first character past the end of the value parsed. +** Report the wrong number of arguments for json_insert(), json_replace() +** or json_set(). +*/ +static void jsonWrongNumArgs( + sqlite3_context *pCtx, + const char *zFuncName +){ + char *zMsg = sqlite3_mprintf("json_%s() needs an odd number of arguments", + zFuncName); + sqlite3_result_error(pCtx, zMsg, -1); + sqlite3_free(zMsg); +} + +/**************************************************************************** +** Utility routines for dealing with the binary BLOB representation of JSON +****************************************************************************/ + +/* +** Expand pParse->aBlob so that it holds at least N bytes. ** -** Special return values: +** Return the number of errors. +*/ +static int jsonBlobExpand(JsonParse *pParse, u32 N){ + u8 *aNew; + u32 t; + assert( N>pParse->nBlobAlloc ); + if( pParse->nBlobAlloc==0 ){ + t = 100; + }else{ + t = pParse->nBlobAlloc*2; + } + if( tdb, pParse->aBlob, t); + if( aNew==0 ){ pParse->oom = 1; return 1; } + pParse->aBlob = aNew; + pParse->nBlobAlloc = t; + return 0; +} + +/* +** If pParse->aBlob is not previously editable (because it is taken +** from sqlite3_value_blob(), as indicated by the fact that +** pParse->nBlobAlloc==0 and pParse->nBlob>0) then make it editable +** by making a copy into space obtained from malloc. +** +** Return true on success. Return false on OOM. +*/ +static int jsonBlobMakeEditable(JsonParse *pParse, u32 nExtra){ + u8 *aOld; + u32 nSize; + assert( !pParse->bReadOnly ); + if( pParse->oom ) return 0; + if( pParse->nBlobAlloc>0 ) return 1; + aOld = pParse->aBlob; + nSize = pParse->nBlob + nExtra; + pParse->aBlob = 0; + if( jsonBlobExpand(pParse, nSize) ){ + return 0; + } + assert( pParse->nBlobAlloc >= pParse->nBlob + nExtra ); + memcpy(pParse->aBlob, aOld, pParse->nBlob); + return 1; +} + +/* Expand pParse->aBlob and append one bytes. +*/ +static SQLITE_NOINLINE void jsonBlobExpandAndAppendOneByte( + JsonParse *pParse, + u8 c +){ + jsonBlobExpand(pParse, pParse->nBlob+1); + if( pParse->oom==0 ){ + assert( pParse->nBlob+1<=pParse->nBlobAlloc ); + pParse->aBlob[pParse->nBlob++] = c; + } +} + +/* Append a single character. +*/ +static void jsonBlobAppendOneByte(JsonParse *pParse, u8 c){ + if( pParse->nBlob >= pParse->nBlobAlloc ){ + jsonBlobExpandAndAppendOneByte(pParse, c); + }else{ + pParse->aBlob[pParse->nBlob++] = c; + } +} + +/* Slow version of jsonBlobAppendNode() that first resizes the +** pParse->aBlob structure. +*/ +static void jsonBlobAppendNode(JsonParse*,u8,u32,const void*); +static SQLITE_NOINLINE void jsonBlobExpandAndAppendNode( + JsonParse *pParse, + u8 eType, + u32 szPayload, + const void *aPayload +){ + if( jsonBlobExpand(pParse, pParse->nBlob+szPayload+9) ) return; + jsonBlobAppendNode(pParse, eType, szPayload, aPayload); +} + + +/* Append an node type byte together with the payload size and +** possibly also the payload. +** +** If aPayload is not NULL, then it is a pointer to the payload which +** is also appended. If aPayload is NULL, the pParse->aBlob[] array +** is resized (if necessary) so that it is big enough to hold the +** payload, but the payload is not appended and pParse->nBlob is left +** pointing to where the first byte of payload will eventually be. +*/ +static void jsonBlobAppendNode( + JsonParse *pParse, /* The JsonParse object under construction */ + u8 eType, /* Node type. One of JSONB_* */ + u32 szPayload, /* Number of bytes of payload */ + const void *aPayload /* The payload. Might be NULL */ +){ + u8 *a; + if( pParse->nBlob+szPayload+9 > pParse->nBlobAlloc ){ + jsonBlobExpandAndAppendNode(pParse,eType,szPayload,aPayload); + return; + } + assert( pParse->aBlob!=0 ); + a = &pParse->aBlob[pParse->nBlob]; + if( szPayload<=11 ){ + a[0] = eType | (szPayload<<4); + pParse->nBlob += 1; + }else if( szPayload<=0xff ){ + a[0] = eType | 0xc0; + a[1] = szPayload & 0xff; + pParse->nBlob += 2; + }else if( szPayload<=0xffff ){ + a[0] = eType | 0xd0; + a[1] = (szPayload >> 8) & 0xff; + a[2] = szPayload & 0xff; + pParse->nBlob += 3; + }else{ + a[0] = eType | 0xe0; + a[1] = (szPayload >> 24) & 0xff; + a[2] = (szPayload >> 16) & 0xff; + a[3] = (szPayload >> 8) & 0xff; + a[4] = szPayload & 0xff; + pParse->nBlob += 5; + } + if( aPayload ){ + pParse->nBlob += szPayload; + memcpy(&pParse->aBlob[pParse->nBlob-szPayload], aPayload, szPayload); + } +} + +/* Change the payload size for the node at index i to be szPayload. +*/ +static int jsonBlobChangePayloadSize( + JsonParse *pParse, + u32 i, + u32 szPayload +){ + u8 *a; + u8 szType; + u8 nExtra; + u8 nNeeded; + int delta; + if( pParse->oom ) return 0; + a = &pParse->aBlob[i]; + szType = a[0]>>4; + if( szType<=11 ){ + nExtra = 0; + }else if( szType==12 ){ + nExtra = 1; + }else if( szType==13 ){ + nExtra = 2; + }else{ + nExtra = 4; + } + if( szPayload<=11 ){ + nNeeded = 0; + }else if( szPayload<=0xff ){ + nNeeded = 1; + }else if( szPayload<=0xffff ){ + nNeeded = 2; + }else{ + nNeeded = 4; + } + delta = nNeeded - nExtra; + if( delta ){ + u32 newSize = pParse->nBlob + delta; + if( delta>0 ){ + if( newSize>pParse->nBlobAlloc && jsonBlobExpand(pParse, newSize) ){ + return 0; /* OOM error. Error state recorded in pParse->oom. */ + } + a = &pParse->aBlob[i]; + memmove(&a[1+delta], &a[1], pParse->nBlob - (i+1)); + }else{ + memmove(&a[1], &a[1-delta], pParse->nBlob - (i+1-delta)); + } + pParse->nBlob = newSize; + } + if( nNeeded==0 ){ + a[0] = (a[0] & 0x0f) | (szPayload<<4); + }else if( nNeeded==1 ){ + a[0] = (a[0] & 0x0f) | 0xc0; + a[1] = szPayload & 0xff; + }else if( nNeeded==2 ){ + a[0] = (a[0] & 0x0f) | 0xd0; + a[1] = (szPayload >> 8) & 0xff; + a[2] = szPayload & 0xff; + }else{ + a[0] = (a[0] & 0x0f) | 0xe0; + a[1] = (szPayload >> 24) & 0xff; + a[2] = (szPayload >> 16) & 0xff; + a[3] = (szPayload >> 8) & 0xff; + a[4] = szPayload & 0xff; + } + return delta; +} + +/* +** If z[0] is 'u' and is followed by exactly 4 hexadecimal character, +** then set *pOp to JSONB_TEXTJ and return true. If not, do not make +** any changes to *pOp and return false. +*/ +static int jsonIs4HexB(const char *z, int *pOp){ + if( z[0]!='u' ) return 0; + if( !jsonIs4Hex(&z[1]) ) return 0; + *pOp = JSONB_TEXTJ; + return 1; +} + +/* +** Check a single element of the JSONB in pParse for validity. +** +** The element to be checked starts at offset i and must end at on the +** last byte before iEnd. +** +** Return 0 if everything is correct. Return the 1-based byte offset of the +** error if a problem is detected. (In other words, if the error is at offset +** 0, return 1). +*/ +static u32 jsonbValidityCheck( + const JsonParse *pParse, /* Input JSONB. Only aBlob and nBlob are used */ + u32 i, /* Start of element as pParse->aBlob[i] */ + u32 iEnd, /* One more than the last byte of the element */ + u32 iDepth /* Current nesting depth */ +){ + u32 n, sz, j, k; + const u8 *z; + u8 x; + if( iDepth>JSON_MAX_DEPTH ) return i+1; + sz = 0; + n = jsonbPayloadSize(pParse, i, &sz); + if( NEVER(n==0) ) return i+1; /* Checked by caller */ + if( NEVER(i+n+sz!=iEnd) ) return i+1; /* Checked by caller */ + z = pParse->aBlob; + x = z[i] & 0x0f; + switch( x ){ + case JSONB_NULL: + case JSONB_TRUE: + case JSONB_FALSE: { + return n+sz==1 ? 0 : i+1; + } + case JSONB_INT: { + if( sz<1 ) return i+1; + j = i+n; + if( z[j]=='-' ){ + j++; + if( sz<2 ) return i+1; + } + k = i+n+sz; + while( jk ) return j+1; + if( z[j+1]!='.' && z[j+1]!='e' && z[j+1]!='E' ) return j+1; + j++; + } + for(; j0 ) return j+1; + if( x==JSONB_FLOAT && (j==k-1 || !sqlite3Isdigit(z[j+1])) ){ + return j+1; + } + seen = 1; + continue; + } + if( z[j]=='e' || z[j]=='E' ){ + if( seen==2 ) return j+1; + if( j==k-1 ) return j+1; + if( z[j+1]=='+' || z[j+1]=='-' ){ + j++; + if( j==k-1 ) return j+1; + } + seen = 2; + continue; + } + return j+1; + } + if( seen==0 ) return i+1; + return 0; + } + case JSONB_TEXT: { + j = i+n; + k = j+sz; + while( j=k ){ + return j+1; + }else if( strchr("\"\\/bfnrt",z[j+1])!=0 ){ + j++; + }else if( z[j+1]=='u' ){ + if( j+5>=k ) return j+1; + if( !jsonIs4Hex((const char*)&z[j+2]) ) return j+1; + j++; + }else if( x!=JSONB_TEXT5 ){ + return j+1; + }else{ + u32 c = 0; + u32 szC = jsonUnescapeOneChar((const char*)&z[j], k-j, &c); + if( c==JSON_INVALID_CHAR ) return j+1; + j += szC - 1; + } + } + j++; + } + return 0; + } + case JSONB_TEXTRAW: { + return 0; + } + case JSONB_ARRAY: { + u32 sub; + j = i+n; + k = j+sz; + while( jk ) return j+1; + sub = jsonbValidityCheck(pParse, j, j+n+sz, iDepth+1); + if( sub ) return sub; + j += n + sz; + } + assert( j==k ); + return 0; + } + case JSONB_OBJECT: { + u32 cnt = 0; + u32 sub; + j = i+n; + k = j+sz; + while( jk ) return j+1; + if( (cnt & 1)==0 ){ + x = z[j] & 0x0f; + if( xJSONB_TEXTRAW ) return j+1; + } + sub = jsonbValidityCheck(pParse, j, j+n+sz, iDepth+1); + if( sub ) return sub; + cnt++; + j += n + sz; + } + assert( j==k ); + if( (cnt & 1)!=0 ) return j+1; + return 0; + } + default: { + return i+1; + } + } +} + +/* +** Translate a single element of JSON text at pParse->zJson[i] into +** its equivalent binary JSONB representation. Append the translation into +** pParse->aBlob[] beginning at pParse->nBlob. The size of +** pParse->aBlob[] is increased as necessary. +** +** Return the index of the first character past the end of the element parsed, +** or one of the following special result codes: ** ** 0 End of input -** -1 Syntax error -** -2 '}' seen -** -3 ']' seen -** -4 ',' seen -** -5 ':' seen +** -1 Syntax error or OOM +** -2 '}' seen \ +** -3 ']' seen \___ For these returns, pParse->iErr is set to +** -4 ',' seen / the index in zJson[] of the seen character +** -5 ':' seen / */ -static int jsonParseValue(JsonParse *pParse, u32 i){ +static int jsonTranslateTextToBlob(JsonParse *pParse, u32 i){ char c; u32 j; - int iThis; + u32 iThis, iStart; int x; - JsonNode *pNode; + u8 t; const char *z = pParse->zJson; json_parse_restart: switch( (u8)z[i] ){ case '{': { /* Parse object */ - iThis = jsonParseAddNode(pParse, JSON_OBJECT, 0, 0); - if( iThis<0 ) return -1; + iThis = pParse->nBlob; + jsonBlobAppendNode(pParse, JSONB_OBJECT, pParse->nJson-i, 0); if( ++pParse->iDepth > JSON_MAX_DEPTH ){ pParse->iErr = i; return -1; } + iStart = pParse->nBlob; for(j=i+1;;j++){ - u32 nNode = pParse->nNode; - x = jsonParseValue(pParse, j); + u32 iBlob = pParse->nBlob; + x = jsonTranslateTextToBlob(pParse, j); if( x<=0 ){ + int op; if( x==(-2) ){ j = pParse->iErr; - if( pParse->nNode!=(u32)iThis+1 ) pParse->hasNonstd = 1; + if( pParse->nBlob!=(u32)iStart ) pParse->hasNonstd = 1; break; } j += json5Whitespace(&z[j]); + op = JSONB_TEXT; if( sqlite3JsonId1(z[j]) - || (z[j]=='\\' && z[j+1]=='u' && jsonIs4Hex(&z[j+2])) + || (z[j]=='\\' && jsonIs4HexB(&z[j+1], &op)) ){ int k = j+1; while( (sqlite3JsonId2(z[k]) && json5Whitespace(&z[k])==0) - || (z[k]=='\\' && z[k+1]=='u' && jsonIs4Hex(&z[k+2])) + || (z[k]=='\\' && jsonIs4HexB(&z[k+1], &op)) ){ k++; } - jsonParseAddNode(pParse, JSON_STRING | (JNODE_RAW<<8), k-j, &z[j]); + assert( iBlob==pParse->nBlob ); + jsonBlobAppendNode(pParse, op, k-j, &z[j]); pParse->hasNonstd = 1; x = k; }else{ @@ -202610,24 +204721,24 @@ json_parse_restart: } } if( pParse->oom ) return -1; - pNode = &pParse->aNode[nNode]; - if( pNode->eType!=JSON_STRING ){ + t = pParse->aBlob[iBlob] & 0x0f; + if( tJSONB_TEXTRAW ){ pParse->iErr = j; return -1; } - pNode->jnFlags |= JNODE_LABEL; j = x; if( z[j]==':' ){ j++; }else{ - if( fast_isspace(z[j]) ){ - do{ j++; }while( fast_isspace(z[j]) ); + if( jsonIsspace(z[j]) ){ + /* strspn() is not helpful here */ + do{ j++; }while( jsonIsspace(z[j]) ); if( z[j]==':' ){ j++; goto parse_object_value; } } - x = jsonParseValue(pParse, j); + x = jsonTranslateTextToBlob(pParse, j); if( x!=(-5) ){ if( x!=(-1) ) pParse->iErr = j; return -1; @@ -202635,7 +204746,7 @@ json_parse_restart: j = pParse->iErr+1; } parse_object_value: - x = jsonParseValue(pParse, j); + x = jsonTranslateTextToBlob(pParse, j); if( x<=0 ){ if( x!=(-1) ) pParse->iErr = j; return -1; @@ -202646,15 +204757,15 @@ json_parse_restart: }else if( z[j]=='}' ){ break; }else{ - if( fast_isspace(z[j]) ){ - do{ j++; }while( fast_isspace(z[j]) ); + if( jsonIsspace(z[j]) ){ + j += 1 + (u32)strspn(&z[j+1], jsonSpaces); if( z[j]==',' ){ continue; }else if( z[j]=='}' ){ break; } } - x = jsonParseValue(pParse, j); + x = jsonTranslateTextToBlob(pParse, j); if( x==(-4) ){ j = pParse->iErr; continue; @@ -202667,25 +204778,26 @@ json_parse_restart: pParse->iErr = j; return -1; } - pParse->aNode[iThis].n = pParse->nNode - (u32)iThis - 1; + jsonBlobChangePayloadSize(pParse, iThis, pParse->nBlob - iStart); pParse->iDepth--; return j+1; } case '[': { /* Parse array */ - iThis = jsonParseAddNode(pParse, JSON_ARRAY, 0, 0); - if( iThis<0 ) return -1; + iThis = pParse->nBlob; + jsonBlobAppendNode(pParse, JSONB_ARRAY, pParse->nJson - i, 0); + iStart = pParse->nBlob; + if( pParse->oom ) return -1; if( ++pParse->iDepth > JSON_MAX_DEPTH ){ pParse->iErr = i; return -1; } - memset(&pParse->aNode[iThis].u, 0, sizeof(pParse->aNode[iThis].u)); for(j=i+1;;j++){ - x = jsonParseValue(pParse, j); + x = jsonTranslateTextToBlob(pParse, j); if( x<=0 ){ if( x==(-3) ){ j = pParse->iErr; - if( pParse->nNode!=(u32)iThis+1 ) pParse->hasNonstd = 1; + if( pParse->nBlob!=iStart ) pParse->hasNonstd = 1; break; } if( x!=(-1) ) pParse->iErr = j; @@ -202697,15 +204809,15 @@ json_parse_restart: }else if( z[j]==']' ){ break; }else{ - if( fast_isspace(z[j]) ){ - do{ j++; }while( fast_isspace(z[j]) ); + if( jsonIsspace(z[j]) ){ + j += 1 + (u32)strspn(&z[j+1], jsonSpaces); if( z[j]==',' ){ continue; }else if( z[j]==']' ){ break; } } - x = jsonParseValue(pParse, j); + x = jsonTranslateTextToBlob(pParse, j); if( x==(-4) ){ j = pParse->iErr; continue; @@ -202718,23 +204830,33 @@ json_parse_restart: pParse->iErr = j; return -1; } - pParse->aNode[iThis].n = pParse->nNode - (u32)iThis - 1; + jsonBlobChangePayloadSize(pParse, iThis, pParse->nBlob - iStart); pParse->iDepth--; return j+1; } case '\'': { - u8 jnFlags; + u8 opcode; char cDelim; pParse->hasNonstd = 1; - jnFlags = JNODE_JSON5; + opcode = JSONB_TEXT; goto parse_string; case '"': /* Parse string */ - jnFlags = 0; + opcode = JSONB_TEXT; parse_string: cDelim = z[i]; - for(j=i+1; 1; j++){ - if( jsonIsOk[(unsigned char)z[j]] ) continue; + j = i+1; + while( 1 /*exit-by-break*/ ){ + if( jsonIsOk[(u8)z[j]] ){ + if( !jsonIsOk[(u8)z[j+1]] ){ + j += 1; + }else if( !jsonIsOk[(u8)z[j+2]] ){ + j += 2; + }else{ + j += 3; + continue; + } + } c = z[j]; if( c==cDelim ){ break; @@ -202743,16 +204865,16 @@ json_parse_restart: if( c=='"' || c=='\\' || c=='/' || c=='b' || c=='f' || c=='n' || c=='r' || c=='t' || (c=='u' && jsonIs4Hex(&z[j+1])) ){ - jnFlags |= JNODE_ESCAPE; + if( opcode==JSONB_TEXT ) opcode = JSONB_TEXTJ; }else if( c=='\'' || c=='0' || c=='v' || c=='\n' || (0xe2==(u8)c && 0x80==(u8)z[j+1] && (0xa8==(u8)z[j+2] || 0xa9==(u8)z[j+2])) || (c=='x' && jsonIs2Hex(&z[j+1])) ){ - jnFlags |= (JNODE_ESCAPE|JNODE_JSON5); + opcode = JSONB_TEXT5; pParse->hasNonstd = 1; }else if( c=='\r' ){ if( z[j+1]=='\n' ) j++; - jnFlags |= (JNODE_ESCAPE|JNODE_JSON5); + opcode = JSONB_TEXT5; pParse->hasNonstd = 1; }else{ pParse->iErr = j; @@ -202762,14 +204884,17 @@ json_parse_restart: /* Control characters are not allowed in strings */ pParse->iErr = j; return -1; + }else if( c=='"' ){ + opcode = JSONB_TEXT5; } + j++; } - jsonParseAddNode(pParse, JSON_STRING | (jnFlags<<8), j+1-i, &z[i]); + jsonBlobAppendNode(pParse, opcode, j-1-i, &z[i+1]); return j+1; } case 't': { if( strncmp(z+i,"true",4)==0 && !sqlite3Isalnum(z[i+4]) ){ - jsonParseAddNode(pParse, JSON_TRUE, 0, 0); + jsonBlobAppendOneByte(pParse, JSONB_TRUE); return i+4; } pParse->iErr = i; @@ -202777,23 +204902,22 @@ json_parse_restart: } case 'f': { if( strncmp(z+i,"false",5)==0 && !sqlite3Isalnum(z[i+5]) ){ - jsonParseAddNode(pParse, JSON_FALSE, 0, 0); + jsonBlobAppendOneByte(pParse, JSONB_FALSE); return i+5; } pParse->iErr = i; return -1; } case '+': { - u8 seenDP, seenE, jnFlags; + u8 seenE; pParse->hasNonstd = 1; - jnFlags = JNODE_JSON5; + t = 0x00; /* Bit 0x01: JSON5. Bit 0x02: FLOAT */ goto parse_number; case '.': if( sqlite3Isdigit(z[i+1]) ){ pParse->hasNonstd = 1; - jnFlags = JNODE_JSON5; + t = 0x03; /* Bit 0x01: JSON5. Bit 0x02: FLOAT */ seenE = 0; - seenDP = JSON_REAL; goto parse_number_2; } pParse->iErr = i; @@ -202810,9 +204934,8 @@ json_parse_restart: case '8': case '9': /* Parse number */ - jnFlags = 0; + t = 0x00; /* Bit 0x01: JSON5. Bit 0x02: FLOAT */ parse_number: - seenDP = JSON_INT; seenE = 0; assert( '-' < '0' ); assert( '+' < '0' ); @@ -202822,9 +204945,9 @@ json_parse_restart: if( c<='0' ){ if( c=='0' ){ if( (z[i+1]=='x' || z[i+1]=='X') && sqlite3Isxdigit(z[i+2]) ){ - assert( seenDP==JSON_INT ); + assert( t==0x00 ); pParse->hasNonstd = 1; - jnFlags |= JNODE_JSON5; + t = 0x01; for(j=i+3; sqlite3Isxdigit(z[j]); j++){} goto parse_number_finish; }else if( sqlite3Isdigit(z[i+1]) ){ @@ -202841,15 +204964,15 @@ json_parse_restart: ){ pParse->hasNonstd = 1; if( z[i]=='-' ){ - jsonParseAddNode(pParse, JSON_REAL, 8, "-9.0e999"); + jsonBlobAppendNode(pParse, JSONB_FLOAT, 6, "-9e999"); }else{ - jsonParseAddNode(pParse, JSON_REAL, 7, "9.0e999"); + jsonBlobAppendNode(pParse, JSONB_FLOAT, 5, "9e999"); } return i + (sqlite3StrNICmp(&z[i+4],"inity",5)==0 ? 9 : 4); } if( z[i+1]=='.' ){ pParse->hasNonstd = 1; - jnFlags |= JNODE_JSON5; + t |= 0x01; goto parse_number_2; } pParse->iErr = i; @@ -202861,30 +204984,31 @@ json_parse_restart: return -1; }else if( (z[i+2]=='x' || z[i+2]=='X') && sqlite3Isxdigit(z[i+3]) ){ pParse->hasNonstd = 1; - jnFlags |= JNODE_JSON5; + t |= 0x01; for(j=i+4; sqlite3Isxdigit(z[j]); j++){} goto parse_number_finish; } } } } + parse_number_2: for(j=i+1;; j++){ c = z[j]; if( sqlite3Isdigit(c) ) continue; if( c=='.' ){ - if( seenDP==JSON_REAL ){ + if( (t & 0x02)!=0 ){ pParse->iErr = j; return -1; } - seenDP = JSON_REAL; + t |= 0x02; continue; } if( c=='e' || c=='E' ){ if( z[j-1]<'0' ){ if( ALWAYS(z[j-1]=='.') && ALWAYS(j-2>=i) && sqlite3Isdigit(z[j-2]) ){ pParse->hasNonstd = 1; - jnFlags |= JNODE_JSON5; + t |= 0x01; }else{ pParse->iErr = j; return -1; @@ -202894,7 +205018,7 @@ json_parse_restart: pParse->iErr = j; return -1; } - seenDP = JSON_REAL; + t |= 0x02; seenE = 1; c = z[j+1]; if( c=='+' || c=='-' ){ @@ -202912,14 +205036,18 @@ json_parse_restart: if( z[j-1]<'0' ){ if( ALWAYS(z[j-1]=='.') && ALWAYS(j-2>=i) && sqlite3Isdigit(z[j-2]) ){ pParse->hasNonstd = 1; - jnFlags |= JNODE_JSON5; + t |= 0x01; }else{ pParse->iErr = j; return -1; } } parse_number_finish: - jsonParseAddNode(pParse, seenDP | (jnFlags<<8), j - i, &z[i]); + assert( JSONB_INT+0x01==JSONB_INT5 ); + assert( JSONB_FLOAT+0x01==JSONB_FLOAT5 ); + assert( JSONB_INT+0x02==JSONB_FLOAT ); + if( z[i]=='+' ) i++; + jsonBlobAppendNode(pParse, JSONB_INT+t, j-i, &z[i]); return j; } case '}': { @@ -202945,9 +205073,7 @@ json_parse_restart: case 0x0a: case 0x0d: case 0x20: { - do{ - i++; - }while( fast_isspace(z[i]) ); + i += 1 + (u32)strspn(&z[i+1], jsonSpaces); goto json_parse_restart; } case 0x0b: @@ -202969,7 +205095,7 @@ json_parse_restart: } case 'n': { if( strncmp(z+i,"null",4)==0 && !sqlite3Isalnum(z[i+4]) ){ - jsonParseAddNode(pParse, JSON_NULL, 0, 0); + jsonBlobAppendOneByte(pParse, JSONB_NULL); return i+4; } /* fall-through into the default case that checks for NaN */ @@ -202985,8 +205111,11 @@ json_parse_restart: continue; } if( sqlite3Isalnum(z[i+nn]) ) continue; - jsonParseAddNode(pParse, aNanInfName[k].eType, - aNanInfName[k].nRepl, aNanInfName[k].zRepl); + if( aNanInfName[k].eType==JSONB_FLOAT ){ + jsonBlobAppendNode(pParse, JSONB_FLOAT, 5, "9e999"); + }else{ + jsonBlobAppendOneByte(pParse, JSONB_NULL); + } pParse->hasNonstd = 1; return i + nn; } @@ -202996,6 +205125,7 @@ json_parse_restart: } /* End switch(z[i]) */ } + /* ** Parse a complete JSON string. Return 0 on success or non-zero if there ** are any errors. If an error occurs, free all memory held by pParse, @@ -203004,20 +205134,26 @@ json_parse_restart: ** pParse must be initialized to an empty parse object prior to calling ** this routine. */ -static int jsonParse( +static int jsonConvertTextToBlob( JsonParse *pParse, /* Initialize and fill this JsonParse object */ sqlite3_context *pCtx /* Report errors here */ ){ int i; const char *zJson = pParse->zJson; - i = jsonParseValue(pParse, 0); + i = jsonTranslateTextToBlob(pParse, 0); if( pParse->oom ) i = -1; if( i>0 ){ +#ifdef SQLITE_DEBUG assert( pParse->iDepth==0 ); - while( fast_isspace(zJson[i]) ) i++; + if( sqlite3Config.bJsonSelfcheck ){ + assert( jsonbValidityCheck(pParse, 0, pParse->nBlob, 0)==0 ); + } +#endif + while( jsonIsspace(zJson[i]) ) i++; if( zJson[i] ){ i += json5Whitespace(&zJson[i]); if( zJson[i] ){ + if( pCtx ) sqlite3_result_error(pCtx, "malformed JSON", -1); jsonParseReset(pParse); return 1; } @@ -203038,248 +205174,715 @@ static int jsonParse( return 0; } - -/* Mark node i of pParse as being a child of iParent. Call recursively -** to fill in all the descendants of node i. +/* +** The input string pStr is a well-formed JSON text string. Convert +** this into the JSONB format and make it the return value of the +** SQL function. */ -static void jsonParseFillInParentage(JsonParse *pParse, u32 i, u32 iParent){ - JsonNode *pNode = &pParse->aNode[i]; - u32 j; - pParse->aUp[i] = iParent; - switch( pNode->eType ){ - case JSON_ARRAY: { - for(j=1; j<=pNode->n; j += jsonNodeSize(pNode+j)){ - jsonParseFillInParentage(pParse, i+j, i); - } - break; - } - case JSON_OBJECT: { - for(j=1; j<=pNode->n; j += jsonNodeSize(pNode+j+1)+1){ - pParse->aUp[i+j] = i; - jsonParseFillInParentage(pParse, i+j+1, i); - } - break; - } - default: { - break; - } +static void jsonReturnStringAsBlob(JsonString *pStr){ + JsonParse px; + memset(&px, 0, sizeof(px)); + jsonStringTerminate(pStr); + px.zJson = pStr->zBuf; + px.nJson = pStr->nUsed; + px.db = sqlite3_context_db_handle(pStr->pCtx); + (void)jsonTranslateTextToBlob(&px, 0); + if( px.oom ){ + sqlite3DbFree(px.db, px.aBlob); + sqlite3_result_error_nomem(pStr->pCtx); + }else{ + assert( px.nBlobAlloc>0 ); + assert( !px.bReadOnly ); + sqlite3_result_blob(pStr->pCtx, px.aBlob, px.nBlob, SQLITE_DYNAMIC); } } -/* -** Compute the parentage of all nodes in a completed parse. +/* The byte at index i is a node type-code. This routine +** determines the payload size for that node and writes that +** payload size in to *pSz. It returns the offset from i to the +** beginning of the payload. Return 0 on error. */ -static int jsonParseFindParents(JsonParse *pParse){ - u32 *aUp; - assert( pParse->aUp==0 ); - aUp = pParse->aUp = sqlite3_malloc64( sizeof(u32)*pParse->nNode ); - if( aUp==0 ){ - pParse->oom = 1; - return SQLITE_NOMEM; - } - jsonParseFillInParentage(pParse, 0, 0); - return SQLITE_OK; -} - -/* -** Magic number used for the JSON parse cache in sqlite3_get_auxdata() -*/ -#define JSON_CACHE_ID (-429938) /* First cache entry */ -#define JSON_CACHE_SZ 4 /* Max number of cache entries */ - -/* -** Obtain a complete parse of the JSON found in the pJson argument -** -** Use the sqlite3_get_auxdata() cache to find a preexisting parse -** if it is available. If the cache is not available or if it -** is no longer valid, parse the JSON again and return the new parse. -** Also register the new parse so that it will be available for -** future sqlite3_get_auxdata() calls. -** -** If an error occurs and pErrCtx!=0 then report the error on pErrCtx -** and return NULL. -** -** The returned pointer (if it is not NULL) is owned by the cache in -** most cases, not the caller. The caller does NOT need to invoke -** jsonParseFree(), in most cases. -** -** Except, if an error occurs and pErrCtx==0 then return the JsonParse -** object with JsonParse.nErr non-zero and the caller will own the JsonParse -** object. In that case, it will be the responsibility of the caller to -** invoke jsonParseFree(). To summarize: -** -** pErrCtx!=0 || p->nErr==0 ==> Return value p is owned by the -** cache. Call does not need to -** free it. -** -** pErrCtx==0 && p->nErr!=0 ==> Return value is owned by the caller -** and so the caller must free it. -*/ -static JsonParse *jsonParseCached( - sqlite3_context *pCtx, /* Context to use for cache search */ - sqlite3_value *pJson, /* Function param containing JSON text */ - sqlite3_context *pErrCtx, /* Write parse errors here if not NULL */ - int bUnedited /* No prior edits allowed */ -){ - char *zJson = (char*)sqlite3_value_text(pJson); - int nJson = sqlite3_value_bytes(pJson); - JsonParse *p; - JsonParse *pMatch = 0; - int iKey; - int iMinKey = 0; - u32 iMinHold = 0xffffffff; - u32 iMaxHold = 0; - int bJsonRCStr; - - if( zJson==0 ) return 0; - for(iKey=0; iKeynJson==nJson - && (p->hasMod==0 || bUnedited==0) - && (p->zJson==zJson || memcmp(p->zJson,zJson,nJson)==0) - ){ - p->nErr = 0; - p->useMod = 0; - pMatch = p; - }else - if( pMatch==0 - && p->zAlt!=0 - && bUnedited==0 - && p->nAlt==nJson - && memcmp(p->zAlt, zJson, nJson)==0 - ){ - p->nErr = 0; - p->useMod = 1; - pMatch = p; - }else if( p->iHoldiHold; - iMinKey = iKey; - } - if( p->iHold>iMaxHold ){ - iMaxHold = p->iHold; - } - } - if( pMatch ){ - /* The input JSON text was found in the cache. Use the preexisting - ** parse of this JSON */ - pMatch->nErr = 0; - pMatch->iHold = iMaxHold+1; - assert( pMatch->nJPRef>0 ); /* pMatch is owned by the cache */ - return pMatch; - } - - /* The input JSON was not found anywhere in the cache. We will need - ** to parse it ourselves and generate a new JsonParse object. - */ - bJsonRCStr = sqlite3ValueIsOfClass(pJson,(void(*)(void*))sqlite3RCStrUnref); - p = sqlite3_malloc64( sizeof(*p) + (bJsonRCStr ? 0 : nJson+1) ); - if( p==0 ){ - sqlite3_result_error_nomem(pCtx); +static u32 jsonbPayloadSize(const JsonParse *pParse, u32 i, u32 *pSz){ + u8 x; + u32 sz; + u32 n; + if( NEVER(i>pParse->nBlob) ){ + *pSz = 0; return 0; } - memset(p, 0, sizeof(*p)); - if( bJsonRCStr ){ - p->zJson = sqlite3RCStrRef(zJson); - p->bJsonIsRCStr = 1; - }else{ - p->zJson = (char*)&p[1]; - memcpy(p->zJson, zJson, nJson+1); - } - p->nJPRef = 1; - if( jsonParse(p, pErrCtx) ){ - if( pErrCtx==0 ){ - p->nErr = 1; - assert( p->nJPRef==1 ); /* Caller will own the new JsonParse object p */ - return p; - } - jsonParseFree(p); - return 0; - } - p->nJson = nJson; - p->iHold = iMaxHold+1; - /* Transfer ownership of the new JsonParse to the cache */ - sqlite3_set_auxdata(pCtx, JSON_CACHE_ID+iMinKey, p, - (void(*)(void*))jsonParseFree); - return (JsonParse*)sqlite3_get_auxdata(pCtx, JSON_CACHE_ID+iMinKey); -} - -/* -** Compare the OBJECT label at pNode against zKey,nKey. Return true on -** a match. -*/ -static int jsonLabelCompare(const JsonNode *pNode, const char *zKey, u32 nKey){ - assert( pNode->eU==1 ); - if( pNode->jnFlags & JNODE_RAW ){ - if( pNode->n!=nKey ) return 0; - return strncmp(pNode->u.zJContent, zKey, nKey)==0; - }else{ - if( pNode->n!=nKey+2 ) return 0; - return strncmp(pNode->u.zJContent+1, zKey, nKey)==0; - } -} -static int jsonSameLabel(const JsonNode *p1, const JsonNode *p2){ - if( p1->jnFlags & JNODE_RAW ){ - return jsonLabelCompare(p2, p1->u.zJContent, p1->n); - }else if( p2->jnFlags & JNODE_RAW ){ - return jsonLabelCompare(p1, p2->u.zJContent, p2->n); - }else{ - return p1->n==p2->n && strncmp(p1->u.zJContent,p2->u.zJContent,p1->n)==0; - } -} - -/* forward declaration */ -static JsonNode *jsonLookupAppend(JsonParse*,const char*,int*,const char**); - -/* -** Search along zPath to find the node specified. Return a pointer -** to that node, or NULL if zPath is malformed or if there is no such -** node. -** -** If pApnd!=0, then try to append new nodes to complete zPath if it is -** possible to do so and if no existing node corresponds to zPath. If -** new nodes are appended *pApnd is set to 1. -*/ -static JsonNode *jsonLookupStep( - JsonParse *pParse, /* The JSON to search */ - u32 iRoot, /* Begin the search at this node */ - const char *zPath, /* The path to search */ - int *pApnd, /* Append nodes to complete path if not NULL */ - const char **pzErr /* Make *pzErr point to any syntax error in zPath */ -){ - u32 i, j, nKey; - const char *zKey; - JsonNode *pRoot; - if( pParse->oom ) return 0; - pRoot = &pParse->aNode[iRoot]; - if( pRoot->jnFlags & (JNODE_REPLACE|JNODE_REMOVE) && pParse->useMod ){ - while( (pRoot->jnFlags & JNODE_REPLACE)!=0 ){ - u32 idx = (u32)(pRoot - pParse->aNode); - i = pParse->iSubst; - while( 1 /*exit-by-break*/ ){ - assert( inNode ); - assert( pParse->aNode[i].eType==JSON_SUBST ); - assert( pParse->aNode[i].eU==4 ); - assert( pParse->aNode[i].u.iPrevaNode[i].n==idx ){ - pRoot = &pParse->aNode[i+1]; - iRoot = i+1; - break; - } - i = pParse->aNode[i].u.iPrev; - } - } - if( pRoot->jnFlags & JNODE_REMOVE ){ + x = pParse->aBlob[i]>>4; + if( x<=11 ){ + sz = x; + n = 1; + }else if( x==12 ){ + if( i+1>=pParse->nBlob ){ + *pSz = 0; return 0; } + sz = pParse->aBlob[i+1]; + n = 2; + }else if( x==13 ){ + if( i+2>=pParse->nBlob ){ + *pSz = 0; + return 0; + } + sz = (pParse->aBlob[i+1]<<8) + pParse->aBlob[i+2]; + n = 3; + }else if( x==14 ){ + if( i+4>=pParse->nBlob ){ + *pSz = 0; + return 0; + } + sz = ((u32)pParse->aBlob[i+1]<<24) + (pParse->aBlob[i+2]<<16) + + (pParse->aBlob[i+3]<<8) + pParse->aBlob[i+4]; + n = 5; + }else{ + if( i+8>=pParse->nBlob + || pParse->aBlob[i+1]!=0 + || pParse->aBlob[i+2]!=0 + || pParse->aBlob[i+3]!=0 + || pParse->aBlob[i+4]!=0 + ){ + *pSz = 0; + return 0; + } + sz = (pParse->aBlob[i+5]<<24) + (pParse->aBlob[i+6]<<16) + + (pParse->aBlob[i+7]<<8) + pParse->aBlob[i+8]; + n = 9; + } + if( (i64)i+sz+n > pParse->nBlob + && (i64)i+sz+n > pParse->nBlob-pParse->delta + ){ + sz = 0; + n = 0; + } + *pSz = sz; + return n; +} + + +/* +** Translate the binary JSONB representation of JSON beginning at +** pParse->aBlob[i] into a JSON text string. Append the JSON +** text onto the end of pOut. Return the index in pParse->aBlob[] +** of the first byte past the end of the element that is translated. +** +** If an error is detected in the BLOB input, the pOut->eErr flag +** might get set to JSTRING_MALFORMED. But not all BLOB input errors +** are detected. So a malformed JSONB input might either result +** in an error, or in incorrect JSON. +** +** The pOut->eErr JSTRING_OOM flag is set on a OOM. +*/ +static u32 jsonTranslateBlobToText( + const JsonParse *pParse, /* the complete parse of the JSON */ + u32 i, /* Start rendering at this index */ + JsonString *pOut /* Write JSON here */ +){ + u32 sz, n, j, iEnd; + + n = jsonbPayloadSize(pParse, i, &sz); + if( n==0 ){ + pOut->eErr |= JSTRING_MALFORMED; + return pParse->nBlob+1; + } + switch( pParse->aBlob[i] & 0x0f ){ + case JSONB_NULL: { + jsonAppendRawNZ(pOut, "null", 4); + return i+1; + } + case JSONB_TRUE: { + jsonAppendRawNZ(pOut, "true", 4); + return i+1; + } + case JSONB_FALSE: { + jsonAppendRawNZ(pOut, "false", 5); + return i+1; + } + case JSONB_INT: + case JSONB_FLOAT: { + if( sz==0 ) goto malformed_jsonb; + jsonAppendRaw(pOut, (const char*)&pParse->aBlob[i+n], sz); + break; + } + case JSONB_INT5: { /* Integer literal in hexadecimal notation */ + u32 k = 2; + sqlite3_uint64 u = 0; + const char *zIn = (const char*)&pParse->aBlob[i+n]; + int bOverflow = 0; + if( sz==0 ) goto malformed_jsonb; + if( zIn[0]=='-' ){ + jsonAppendChar(pOut, '-'); + k++; + }else if( zIn[0]=='+' ){ + k++; + } + for(; keErr |= JSTRING_MALFORMED; + break; + }else if( (u>>60)!=0 ){ + bOverflow = 1; + }else{ + u = u*16 + sqlite3HexToInt(zIn[k]); + } + } + jsonPrintf(100,pOut,bOverflow?"9.0e999":"%llu", u); + break; + } + case JSONB_FLOAT5: { /* Float literal missing digits beside "." */ + u32 k = 0; + const char *zIn = (const char*)&pParse->aBlob[i+n]; + if( sz==0 ) goto malformed_jsonb; + if( zIn[0]=='-' ){ + jsonAppendChar(pOut, '-'); + k++; + } + if( zIn[k]=='.' ){ + jsonAppendChar(pOut, '0'); + } + for(; kaBlob[i+n], sz); + jsonAppendChar(pOut, '"'); + break; + } + case JSONB_TEXT5: { + const char *zIn; + u32 k; + u32 sz2 = sz; + zIn = (const char*)&pParse->aBlob[i+n]; + jsonAppendChar(pOut, '"'); + while( sz2>0 ){ + for(k=0; k0 ){ + jsonAppendRawNZ(pOut, zIn, k); + if( k>=sz2 ){ + break; + } + zIn += k; + sz2 -= k; + } + if( zIn[0]=='"' ){ + jsonAppendRawNZ(pOut, "\\\"", 2); + zIn++; + sz2--; + continue; + } + assert( zIn[0]=='\\' ); + assert( sz2>=1 ); + if( sz2<2 ){ + pOut->eErr |= JSTRING_MALFORMED; + break; + } + switch( (u8)zIn[1] ){ + case '\'': + jsonAppendChar(pOut, '\''); + break; + case 'v': + jsonAppendRawNZ(pOut, "\\u0009", 6); + break; + case 'x': + if( sz2<4 ){ + pOut->eErr |= JSTRING_MALFORMED; + sz2 = 2; + break; + } + jsonAppendRawNZ(pOut, "\\u00", 4); + jsonAppendRawNZ(pOut, &zIn[2], 2); + zIn += 2; + sz2 -= 2; + break; + case '0': + jsonAppendRawNZ(pOut, "\\u0000", 6); + break; + case '\r': + if( sz2>2 && zIn[2]=='\n' ){ + zIn++; + sz2--; + } + break; + case '\n': + break; + case 0xe2: + /* '\' followed by either U+2028 or U+2029 is ignored as + ** whitespace. Not that in UTF8, U+2028 is 0xe2 0x80 0x29. + ** U+2029 is the same except for the last byte */ + if( sz2<4 + || 0x80!=(u8)zIn[2] + || (0xa8!=(u8)zIn[3] && 0xa9!=(u8)zIn[3]) + ){ + pOut->eErr |= JSTRING_MALFORMED; + sz2 = 2; + break; + } + zIn += 2; + sz2 -= 2; + break; + default: + jsonAppendRawNZ(pOut, zIn, 2); + break; + } + assert( sz2>=2 ); + zIn += 2; + sz2 -= 2; + } + jsonAppendChar(pOut, '"'); + break; + } + case JSONB_TEXTRAW: { + jsonAppendString(pOut, (const char*)&pParse->aBlob[i+n], sz); + break; + } + case JSONB_ARRAY: { + jsonAppendChar(pOut, '['); + j = i+n; + iEnd = j+sz; + while( jeErr==0 ){ + j = jsonTranslateBlobToText(pParse, j, pOut); + jsonAppendChar(pOut, ','); + } + if( j>iEnd ) pOut->eErr |= JSTRING_MALFORMED; + if( sz>0 ) jsonStringTrimOneChar(pOut); + jsonAppendChar(pOut, ']'); + break; + } + case JSONB_OBJECT: { + int x = 0; + jsonAppendChar(pOut, '{'); + j = i+n; + iEnd = j+sz; + while( jeErr==0 ){ + j = jsonTranslateBlobToText(pParse, j, pOut); + jsonAppendChar(pOut, (x++ & 1) ? ',' : ':'); + } + if( (x & 1)!=0 || j>iEnd ) pOut->eErr |= JSTRING_MALFORMED; + if( sz>0 ) jsonStringTrimOneChar(pOut); + jsonAppendChar(pOut, '}'); + break; + } + + default: { + malformed_jsonb: + pOut->eErr |= JSTRING_MALFORMED; + break; + } + } + return i+n+sz; +} + +/* Return true if the input pJson +** +** For performance reasons, this routine does not do a detailed check of the +** input BLOB to ensure that it is well-formed. Hence, false positives are +** possible. False negatives should never occur, however. +*/ +static int jsonFuncArgMightBeBinary(sqlite3_value *pJson){ + u32 sz, n; + const u8 *aBlob; + int nBlob; + JsonParse s; + if( sqlite3_value_type(pJson)!=SQLITE_BLOB ) return 0; + aBlob = sqlite3_value_blob(pJson); + nBlob = sqlite3_value_bytes(pJson); + if( nBlob<1 ) return 0; + if( NEVER(aBlob==0) || (aBlob[0] & 0x0f)>JSONB_OBJECT ) return 0; + memset(&s, 0, sizeof(s)); + s.aBlob = (u8*)aBlob; + s.nBlob = nBlob; + n = jsonbPayloadSize(&s, 0, &sz); + if( n==0 ) return 0; + if( sz+n!=(u32)nBlob ) return 0; + if( (aBlob[0] & 0x0f)<=JSONB_FALSE && sz>0 ) return 0; + return sz+n==(u32)nBlob; +} + +/* +** Given that a JSONB_ARRAY object starts at offset i, return +** the number of entries in that array. +*/ +static u32 jsonbArrayCount(JsonParse *pParse, u32 iRoot){ + u32 n, sz, i, iEnd; + u32 k = 0; + n = jsonbPayloadSize(pParse, iRoot, &sz); + iEnd = iRoot+n+sz; + for(i=iRoot+n; n>0 && idelta. +*/ +static void jsonAfterEditSizeAdjust(JsonParse *pParse, u32 iRoot){ + u32 sz = 0; + u32 nBlob; + assert( pParse->delta!=0 ); + assert( pParse->nBlobAlloc >= pParse->nBlob ); + nBlob = pParse->nBlob; + pParse->nBlob = pParse->nBlobAlloc; + (void)jsonbPayloadSize(pParse, iRoot, &sz); + pParse->nBlob = nBlob; + sz += pParse->delta; + pParse->delta += jsonBlobChangePayloadSize(pParse, iRoot, sz); +} + +/* +** Modify the JSONB blob at pParse->aBlob by removing nDel bytes of +** content beginning at iDel, and replacing them with nIns bytes of +** content given by aIns. +** +** nDel may be zero, in which case no bytes are removed. But iDel is +** still important as new bytes will be insert beginning at iDel. +** +** aIns may be zero, in which case space is created to hold nIns bytes +** beginning at iDel, but that space is uninitialized. +** +** Set pParse->oom if an OOM occurs. +*/ +static void jsonBlobEdit( + JsonParse *pParse, /* The JSONB to be modified is in pParse->aBlob */ + u32 iDel, /* First byte to be removed */ + u32 nDel, /* Number of bytes to remove */ + const u8 *aIns, /* Content to insert */ + u32 nIns /* Bytes of content to insert */ +){ + i64 d = (i64)nIns - (i64)nDel; + if( d!=0 ){ + if( pParse->nBlob + d > pParse->nBlobAlloc ){ + jsonBlobExpand(pParse, pParse->nBlob+d); + if( pParse->oom ) return; + } + memmove(&pParse->aBlob[iDel+nIns], + &pParse->aBlob[iDel+nDel], + pParse->nBlob - (iDel+nDel)); + pParse->nBlob += d; + pParse->delta += d; + } + if( nIns && aIns ) memcpy(&pParse->aBlob[iDel], aIns, nIns); +} + +/* +** Return the number of escaped newlines to be ignored. +** An escaped newline is a one of the following byte sequences: +** +** 0x5c 0x0a +** 0x5c 0x0d +** 0x5c 0x0d 0x0a +** 0x5c 0xe2 0x80 0xa8 +** 0x5c 0xe2 0x80 0xa9 +*/ +static u32 jsonBytesToBypass(const char *z, u32 n){ + u32 i = 0; + while( i+10 ); + assert( z[0]=='\\' ); + if( n<2 ){ + *piOut = JSON_INVALID_CHAR; + return n; + } + switch( (u8)z[1] ){ + case 'u': { + u32 v, vlo; + if( n<6 ){ + *piOut = JSON_INVALID_CHAR; + return n; + } + v = jsonHexToInt4(&z[2]); + if( (v & 0xfc00)==0xd800 + && n>=12 + && z[6]=='\\' + && z[7]=='u' + && ((vlo = jsonHexToInt4(&z[8]))&0xfc00)==0xdc00 + ){ + *piOut = ((v&0x3ff)<<10) + (vlo&0x3ff) + 0x10000; + return 12; + }else{ + *piOut = v; + return 6; + } + } + case 'b': { *piOut = '\b'; return 2; } + case 'f': { *piOut = '\f'; return 2; } + case 'n': { *piOut = '\n'; return 2; } + case 'r': { *piOut = '\r'; return 2; } + case 't': { *piOut = '\t'; return 2; } + case 'v': { *piOut = '\v'; return 2; } + case '0': { *piOut = 0; return 2; } + case '\'': + case '"': + case '/': + case '\\':{ *piOut = z[1]; return 2; } + case 'x': { + if( n<4 ){ + *piOut = JSON_INVALID_CHAR; + return n; + } + *piOut = (jsonHexToInt(z[2])<<4) | jsonHexToInt(z[3]); + return 4; + } + case 0xe2: + case '\r': + case '\n': { + u32 nSkip = jsonBytesToBypass(z, n); + if( nSkip==0 ){ + *piOut = JSON_INVALID_CHAR; + return n; + }else if( nSkip==n ){ + *piOut = 0; + return n; + }else if( z[nSkip]=='\\' ){ + return nSkip + jsonUnescapeOneChar(&z[nSkip], n-nSkip, piOut); + }else{ + int sz = sqlite3Utf8ReadLimited((u8*)&z[nSkip], n-nSkip, piOut); + return nSkip + sz; + } + } + default: { + *piOut = JSON_INVALID_CHAR; + return 2; + } + } +} + + +/* +** Compare two object labels. Return 1 if they are equal and +** 0 if they differ. +** +** In this version, we know that one or the other or both of the +** two comparands contains an escape sequence. +*/ +static SQLITE_NOINLINE int jsonLabelCompareEscaped( + const char *zLeft, /* The left label */ + u32 nLeft, /* Size of the left label in bytes */ + int rawLeft, /* True if zLeft contains no escapes */ + const char *zRight, /* The right label */ + u32 nRight, /* Size of the right label in bytes */ + int rawRight /* True if zRight is escape-free */ +){ + u32 cLeft, cRight; + assert( rawLeft==0 || rawRight==0 ); + while( 1 /*exit-by-return*/ ){ + if( nLeft==0 ){ + cLeft = 0; + }else if( rawLeft || zLeft[0]!='\\' ){ + cLeft = ((u8*)zLeft)[0]; + if( cLeft>=0xc0 ){ + int sz = sqlite3Utf8ReadLimited((u8*)zLeft, nLeft, &cLeft); + zLeft += sz; + nLeft -= sz; + }else{ + zLeft++; + nLeft--; + } + }else{ + u32 n = jsonUnescapeOneChar(zLeft, nLeft, &cLeft); + zLeft += n; + assert( n<=nLeft ); + nLeft -= n; + } + if( nRight==0 ){ + cRight = 0; + }else if( rawRight || zRight[0]!='\\' ){ + cRight = ((u8*)zRight)[0]; + if( cRight>=0xc0 ){ + int sz = sqlite3Utf8ReadLimited((u8*)zRight, nRight, &cRight); + zRight += sz; + nRight -= sz; + }else{ + zRight++; + nRight--; + } + }else{ + u32 n = jsonUnescapeOneChar(zRight, nRight, &cRight); + zRight += n; + assert( n<=nRight ); + nRight -= n; + } + if( cLeft!=cRight ) return 0; + if( cLeft==0 ) return 1; + } +} + +/* +** Compare two object labels. Return 1 if they are equal and +** 0 if they differ. Return -1 if an OOM occurs. +*/ +static int jsonLabelCompare( + const char *zLeft, /* The left label */ + u32 nLeft, /* Size of the left label in bytes */ + int rawLeft, /* True if zLeft contains no escapes */ + const char *zRight, /* The right label */ + u32 nRight, /* Size of the right label in bytes */ + int rawRight /* True if zRight is escape-free */ +){ + if( rawLeft && rawRight ){ + /* Simpliest case: Neither label contains escapes. A simple + ** memcmp() is sufficient. */ + if( nLeft!=nRight ) return 0; + return memcmp(zLeft, zRight, nLeft)==0; + }else{ + return jsonLabelCompareEscaped(zLeft, nLeft, rawLeft, + zRight, nRight, rawRight); + } +} + +/* +** Error returns from jsonLookupStep() +*/ +#define JSON_LOOKUP_ERROR 0xffffffff +#define JSON_LOOKUP_NOTFOUND 0xfffffffe +#define JSON_LOOKUP_PATHERROR 0xfffffffd +#define JSON_LOOKUP_ISERROR(x) ((x)>=JSON_LOOKUP_PATHERROR) + +/* Forward declaration */ +static u32 jsonLookupStep(JsonParse*,u32,const char*,u32); + + +/* This helper routine for jsonLookupStep() populates pIns with +** binary data that is to be inserted into pParse. +** +** In the common case, pIns just points to pParse->aIns and pParse->nIns. +** But if the zPath of the original edit operation includes path elements +** that go deeper, additional substructure must be created. +** +** For example: +** +** json_insert('{}', '$.a.b.c', 123); +** +** The search stops at '$.a' But additional substructure must be +** created for the ".b.c" part of the patch so that the final result +** is: {"a":{"b":{"c"::123}}}. This routine populates pIns with +** the binary equivalent of {"b":{"c":123}} so that it can be inserted. +** +** The caller is responsible for resetting pIns when it has finished +** using the substructure. +*/ +static u32 jsonCreateEditSubstructure( + JsonParse *pParse, /* The original JSONB that is being edited */ + JsonParse *pIns, /* Populate this with the blob data to insert */ + const char *zTail /* Tail of the path that determins substructure */ +){ + static const u8 emptyObject[] = { JSONB_ARRAY, JSONB_OBJECT }; + int rc; + memset(pIns, 0, sizeof(*pIns)); + pIns->db = pParse->db; + if( zTail[0]==0 ){ + /* No substructure. Just insert what is given in pParse. */ + pIns->aBlob = pParse->aIns; + pIns->nBlob = pParse->nIns; + rc = 0; + }else{ + /* Construct the binary substructure */ + pIns->nBlob = 1; + pIns->aBlob = (u8*)&emptyObject[zTail[0]=='.']; + pIns->eEdit = pParse->eEdit; + pIns->nIns = pParse->nIns; + pIns->aIns = pParse->aIns; + rc = jsonLookupStep(pIns, 0, zTail, 0); + pParse->oom |= pIns->oom; + } + return rc; /* Error code only */ +} + +/* +** Search along zPath to find the Json element specified. Return an +** index into pParse->aBlob[] for the start of that element's value. +** +** If the value found by this routine is the value half of label/value pair +** within an object, then set pPath->iLabel to the start of the corresponding +** label, before returning. +** +** Return one of the JSON_LOOKUP error codes if problems are seen. +** +** This routine will also modify the blob. If pParse->eEdit is one of +** JEDIT_DEL, JEDIT_REPL, JEDIT_INS, or JEDIT_SET, then changes might be +** made to the selected value. If an edit is performed, then the return +** value does not necessarily point to the select element. If an edit +** is performed, the return value is only useful for detecting error +** conditions. +*/ +static u32 jsonLookupStep( + JsonParse *pParse, /* The JSON to search */ + u32 iRoot, /* Begin the search at this element of aBlob[] */ + const char *zPath, /* The path to search */ + u32 iLabel /* Label if iRoot is a value of in an object */ +){ + u32 i, j, k, nKey, sz, n, iEnd, rc; + const char *zKey; + u8 x; + + if( zPath[0]==0 ){ + if( pParse->eEdit && jsonBlobMakeEditable(pParse, pParse->nIns) ){ + n = jsonbPayloadSize(pParse, iRoot, &sz); + sz += n; + if( pParse->eEdit==JEDIT_DEL ){ + if( iLabel>0 ){ + sz += iRoot - iLabel; + iRoot = iLabel; + } + jsonBlobEdit(pParse, iRoot, sz, 0, 0); + }else if( pParse->eEdit==JEDIT_INS ){ + /* Already exists, so json_insert() is a no-op */ + }else{ + /* json_set() or json_replace() */ + jsonBlobEdit(pParse, iRoot, sz, pParse->aIns, pParse->nIns); + } + } + pParse->iLabel = iLabel; + return iRoot; } - if( zPath[0]==0 ) return pRoot; if( zPath[0]=='.' ){ - if( pRoot->eType!=JSON_OBJECT ) return 0; + int rawKey = 1; + x = pParse->aBlob[iRoot]; zPath++; if( zPath[0]=='"' ){ zKey = zPath + 1; @@ -203288,250 +205891,704 @@ static JsonNode *jsonLookupStep( if( zPath[i] ){ i++; }else{ - *pzErr = zPath; - return 0; + return JSON_LOOKUP_PATHERROR; } testcase( nKey==0 ); + rawKey = memchr(zKey, '\\', nKey)==0; }else{ zKey = zPath; for(i=0; zPath[i] && zPath[i]!='.' && zPath[i]!='['; i++){} nKey = i; if( nKey==0 ){ - *pzErr = zPath; - return 0; + return JSON_LOOKUP_PATHERROR; } } - j = 1; - for(;;){ - while( j<=pRoot->n ){ - if( jsonLabelCompare(pRoot+j, zKey, nKey) ){ - return jsonLookupStep(pParse, iRoot+j+1, &zPath[i], pApnd, pzErr); + if( (x & 0x0f)!=JSONB_OBJECT ) return JSON_LOOKUP_NOTFOUND; + n = jsonbPayloadSize(pParse, iRoot, &sz); + j = iRoot + n; /* j is the index of a label */ + iEnd = j+sz; + while( jaBlob[j] & 0x0f; + if( xJSONB_TEXTRAW ) return JSON_LOOKUP_ERROR; + n = jsonbPayloadSize(pParse, j, &sz); + if( n==0 ) return JSON_LOOKUP_ERROR; + k = j+n; /* k is the index of the label text */ + if( k+sz>=iEnd ) return JSON_LOOKUP_ERROR; + zLabel = (const char*)&pParse->aBlob[k]; + rawLabel = x==JSONB_TEXT || x==JSONB_TEXTRAW; + if( jsonLabelCompare(zKey, nKey, rawKey, zLabel, sz, rawLabel) ){ + u32 v = k+sz; /* v is the index of the value */ + if( ((pParse->aBlob[v])&0x0f)>JSONB_OBJECT ) return JSON_LOOKUP_ERROR; + n = jsonbPayloadSize(pParse, v, &sz); + if( n==0 || v+n+sz>iEnd ) return JSON_LOOKUP_ERROR; + assert( j>0 ); + rc = jsonLookupStep(pParse, v, &zPath[i], j); + if( pParse->delta ) jsonAfterEditSizeAdjust(pParse, iRoot); + return rc; + } + j = k+sz; + if( ((pParse->aBlob[j])&0x0f)>JSONB_OBJECT ) return JSON_LOOKUP_ERROR; + n = jsonbPayloadSize(pParse, j, &sz); + if( n==0 ) return JSON_LOOKUP_ERROR; + j += n+sz; + } + if( j>iEnd ) return JSON_LOOKUP_ERROR; + if( pParse->eEdit>=JEDIT_INS ){ + u32 nIns; /* Total bytes to insert (label+value) */ + JsonParse v; /* BLOB encoding of the value to be inserted */ + JsonParse ix; /* Header of the label to be inserted */ + testcase( pParse->eEdit==JEDIT_INS ); + testcase( pParse->eEdit==JEDIT_SET ); + memset(&ix, 0, sizeof(ix)); + ix.db = pParse->db; + jsonBlobAppendNode(&ix, rawKey?JSONB_TEXTRAW:JSONB_TEXT5, nKey, 0); + pParse->oom |= ix.oom; + rc = jsonCreateEditSubstructure(pParse, &v, &zPath[i]); + if( !JSON_LOOKUP_ISERROR(rc) + && jsonBlobMakeEditable(pParse, ix.nBlob+nKey+v.nBlob) + ){ + assert( !pParse->oom ); + nIns = ix.nBlob + nKey + v.nBlob; + jsonBlobEdit(pParse, j, 0, 0, nIns); + if( !pParse->oom ){ + assert( pParse->aBlob!=0 ); /* Because pParse->oom!=0 */ + assert( ix.aBlob!=0 ); /* Because pPasre->oom!=0 */ + memcpy(&pParse->aBlob[j], ix.aBlob, ix.nBlob); + k = j + ix.nBlob; + memcpy(&pParse->aBlob[k], zKey, nKey); + k += nKey; + memcpy(&pParse->aBlob[k], v.aBlob, v.nBlob); + if( ALWAYS(pParse->delta) ) jsonAfterEditSizeAdjust(pParse, iRoot); } - j++; - j += jsonNodeSize(&pRoot[j]); } - if( (pRoot->jnFlags & JNODE_APPEND)==0 ) break; - if( pParse->useMod==0 ) break; - assert( pRoot->eU==2 ); - iRoot = pRoot->u.iAppend; - pRoot = &pParse->aNode[iRoot]; - j = 1; - } - if( pApnd ){ - u32 iStart, iLabel; - JsonNode *pNode; - assert( pParse->useMod ); - iStart = jsonParseAddNode(pParse, JSON_OBJECT, 2, 0); - iLabel = jsonParseAddNode(pParse, JSON_STRING, nKey, zKey); - zPath += i; - pNode = jsonLookupAppend(pParse, zPath, pApnd, pzErr); - if( pParse->oom ) return 0; - if( pNode ){ - pRoot = &pParse->aNode[iRoot]; - assert( pRoot->eU==0 ); - pRoot->u.iAppend = iStart; - pRoot->jnFlags |= JNODE_APPEND; - VVA( pRoot->eU = 2 ); - pParse->aNode[iLabel].jnFlags |= JNODE_RAW; - } - return pNode; + jsonParseReset(&v); + jsonParseReset(&ix); + return rc; } }else if( zPath[0]=='[' ){ - i = 0; - j = 1; - while( sqlite3Isdigit(zPath[j]) ){ - i = i*10 + zPath[j] - '0'; - j++; + x = pParse->aBlob[iRoot] & 0x0f; + if( x!=JSONB_ARRAY ) return JSON_LOOKUP_NOTFOUND; + n = jsonbPayloadSize(pParse, iRoot, &sz); + k = 0; + i = 1; + while( sqlite3Isdigit(zPath[i]) ){ + k = k*10 + zPath[i] - '0'; + i++; } - if( j<2 || zPath[j]!=']' ){ + if( i<2 || zPath[i]!=']' ){ if( zPath[1]=='#' ){ - JsonNode *pBase = pRoot; - int iBase = iRoot; - if( pRoot->eType!=JSON_ARRAY ) return 0; - for(;;){ - while( j<=pBase->n ){ - if( (pBase[j].jnFlags & JNODE_REMOVE)==0 || pParse->useMod==0 ) i++; - j += jsonNodeSize(&pBase[j]); - } - if( (pBase->jnFlags & JNODE_APPEND)==0 ) break; - if( pParse->useMod==0 ) break; - assert( pBase->eU==2 ); - iBase = pBase->u.iAppend; - pBase = &pParse->aNode[iBase]; - j = 1; - } - j = 2; + k = jsonbArrayCount(pParse, iRoot); + i = 2; if( zPath[2]=='-' && sqlite3Isdigit(zPath[3]) ){ - unsigned int x = 0; - j = 3; + unsigned int nn = 0; + i = 3; do{ - x = x*10 + zPath[j] - '0'; - j++; - }while( sqlite3Isdigit(zPath[j]) ); - if( x>i ) return 0; - i -= x; + nn = nn*10 + zPath[i] - '0'; + i++; + }while( sqlite3Isdigit(zPath[i]) ); + if( nn>k ) return JSON_LOOKUP_NOTFOUND; + k -= nn; } - if( zPath[j]!=']' ){ - *pzErr = zPath; - return 0; + if( zPath[i]!=']' ){ + return JSON_LOOKUP_PATHERROR; } }else{ - *pzErr = zPath; - return 0; + return JSON_LOOKUP_PATHERROR; } } - if( pRoot->eType!=JSON_ARRAY ) return 0; - zPath += j + 1; - j = 1; - for(;;){ - while( j<=pRoot->n - && (i>0 || ((pRoot[j].jnFlags & JNODE_REMOVE)!=0 && pParse->useMod)) + j = iRoot+n; + iEnd = j+sz; + while( jdelta ) jsonAfterEditSizeAdjust(pParse, iRoot); + return rc; + } + k--; + n = jsonbPayloadSize(pParse, j, &sz); + if( n==0 ) return JSON_LOOKUP_ERROR; + j += n+sz; + } + if( j>iEnd ) return JSON_LOOKUP_ERROR; + if( k>0 ) return JSON_LOOKUP_NOTFOUND; + if( pParse->eEdit>=JEDIT_INS ){ + JsonParse v; + testcase( pParse->eEdit==JEDIT_INS ); + testcase( pParse->eEdit==JEDIT_SET ); + rc = jsonCreateEditSubstructure(pParse, &v, &zPath[i+1]); + if( !JSON_LOOKUP_ISERROR(rc) + && jsonBlobMakeEditable(pParse, v.nBlob) ){ - if( (pRoot[j].jnFlags & JNODE_REMOVE)==0 || pParse->useMod==0 ) i--; - j += jsonNodeSize(&pRoot[j]); + assert( !pParse->oom ); + jsonBlobEdit(pParse, j, 0, v.aBlob, v.nBlob); } - if( (pRoot->jnFlags & JNODE_APPEND)==0 ) break; - if( pParse->useMod==0 ) break; - assert( pRoot->eU==2 ); - iRoot = pRoot->u.iAppend; - pRoot = &pParse->aNode[iRoot]; - j = 1; - } - if( j<=pRoot->n ){ - return jsonLookupStep(pParse, iRoot+j, zPath, pApnd, pzErr); - } - if( i==0 && pApnd ){ - u32 iStart; - JsonNode *pNode; - assert( pParse->useMod ); - iStart = jsonParseAddNode(pParse, JSON_ARRAY, 1, 0); - pNode = jsonLookupAppend(pParse, zPath, pApnd, pzErr); - if( pParse->oom ) return 0; - if( pNode ){ - pRoot = &pParse->aNode[iRoot]; - assert( pRoot->eU==0 ); - pRoot->u.iAppend = iStart; - pRoot->jnFlags |= JNODE_APPEND; - VVA( pRoot->eU = 2 ); - } - return pNode; + jsonParseReset(&v); + if( pParse->delta ) jsonAfterEditSizeAdjust(pParse, iRoot); + return rc; } }else{ - *pzErr = zPath; + return JSON_LOOKUP_PATHERROR; } - return 0; + return JSON_LOOKUP_NOTFOUND; } /* -** Append content to pParse that will complete zPath. Return a pointer -** to the inserted node, or return NULL if the append fails. +** Convert a JSON BLOB into text and make that text the return value +** of an SQL function. */ -static JsonNode *jsonLookupAppend( - JsonParse *pParse, /* Append content to the JSON parse */ - const char *zPath, /* Description of content to append */ - int *pApnd, /* Set this flag to 1 */ - const char **pzErr /* Make this point to any syntax error */ +static void jsonReturnTextJsonFromBlob( + sqlite3_context *ctx, + const u8 *aBlob, + u32 nBlob ){ - *pApnd = 1; - if( zPath[0]==0 ){ - jsonParseAddNode(pParse, JSON_NULL, 0, 0); - return pParse->oom ? 0 : &pParse->aNode[pParse->nNode-1]; + JsonParse x; + JsonString s; + + if( NEVER(aBlob==0) ) return; + memset(&x, 0, sizeof(x)); + x.aBlob = (u8*)aBlob; + x.nBlob = nBlob; + jsonStringInit(&s, ctx); + jsonTranslateBlobToText(&x, 0, &s); + jsonReturnString(&s, 0, 0); +} + + +/* +** Return the value of the BLOB node at index i. +** +** If the value is a primitive, return it as an SQL value. +** If the value is an array or object, return it as either +** JSON text or the BLOB encoding, depending on the JSON_B flag +** on the userdata. +*/ +static void jsonReturnFromBlob( + JsonParse *pParse, /* Complete JSON parse tree */ + u32 i, /* Index of the node */ + sqlite3_context *pCtx, /* Return value for this function */ + int textOnly /* return text JSON. Disregard user-data */ +){ + u32 n, sz; + int rc; + sqlite3 *db = sqlite3_context_db_handle(pCtx); + + n = jsonbPayloadSize(pParse, i, &sz); + if( n==0 ){ + sqlite3_result_error(pCtx, "malformed JSON", -1); + return; } - if( zPath[0]=='.' ){ - jsonParseAddNode(pParse, JSON_OBJECT, 0, 0); - }else if( strncmp(zPath,"[0]",3)==0 ){ - jsonParseAddNode(pParse, JSON_ARRAY, 0, 0); + switch( pParse->aBlob[i] & 0x0f ){ + case JSONB_NULL: { + if( sz ) goto returnfromblob_malformed; + sqlite3_result_null(pCtx); + break; + } + case JSONB_TRUE: { + if( sz ) goto returnfromblob_malformed; + sqlite3_result_int(pCtx, 1); + break; + } + case JSONB_FALSE: { + if( sz ) goto returnfromblob_malformed; + sqlite3_result_int(pCtx, 0); + break; + } + case JSONB_INT5: + case JSONB_INT: { + sqlite3_int64 iRes = 0; + char *z; + int bNeg = 0; + char x; + if( sz==0 ) goto returnfromblob_malformed; + x = (char)pParse->aBlob[i+n]; + if( x=='-' ){ + if( sz<2 ) goto returnfromblob_malformed; + n++; + sz--; + bNeg = 1; + } + z = sqlite3DbStrNDup(db, (const char*)&pParse->aBlob[i+n], (int)sz); + if( z==0 ) goto returnfromblob_oom; + rc = sqlite3DecOrHexToI64(z, &iRes); + sqlite3DbFree(db, z); + if( rc==0 ){ + sqlite3_result_int64(pCtx, bNeg ? -iRes : iRes); + }else if( rc==3 && bNeg ){ + sqlite3_result_int64(pCtx, SMALLEST_INT64); + }else if( rc==1 ){ + goto returnfromblob_malformed; + }else{ + if( bNeg ){ n--; sz++; } + goto to_double; + } + break; + } + case JSONB_FLOAT5: + case JSONB_FLOAT: { + double r; + char *z; + if( sz==0 ) goto returnfromblob_malformed; + to_double: + z = sqlite3DbStrNDup(db, (const char*)&pParse->aBlob[i+n], (int)sz); + if( z==0 ) goto returnfromblob_oom; + rc = sqlite3AtoF(z, &r, sqlite3Strlen30(z), SQLITE_UTF8); + sqlite3DbFree(db, z); + if( rc<=0 ) goto returnfromblob_malformed; + sqlite3_result_double(pCtx, r); + break; + } + case JSONB_TEXTRAW: + case JSONB_TEXT: { + sqlite3_result_text(pCtx, (char*)&pParse->aBlob[i+n], sz, + SQLITE_TRANSIENT); + break; + } + case JSONB_TEXT5: + case JSONB_TEXTJ: { + /* Translate JSON formatted string into raw text */ + u32 iIn, iOut; + const char *z; + char *zOut; + u32 nOut = sz; + z = (const char*)&pParse->aBlob[i+n]; + zOut = sqlite3DbMallocRaw(db, nOut+1); + if( zOut==0 ) goto returnfromblob_oom; + for(iIn=iOut=0; iIn=2 ); + zOut[iOut++] = (char)(0xc0 | (v>>6)); + zOut[iOut++] = 0x80 | (v&0x3f); + }else if( v<0x10000 ){ + assert( szEscape>=3 ); + zOut[iOut++] = 0xe0 | (v>>12); + zOut[iOut++] = 0x80 | ((v>>6)&0x3f); + zOut[iOut++] = 0x80 | (v&0x3f); + }else if( v==JSON_INVALID_CHAR ){ + /* Silently ignore illegal unicode */ + }else{ + assert( szEscape>=4 ); + zOut[iOut++] = 0xf0 | (v>>18); + zOut[iOut++] = 0x80 | ((v>>12)&0x3f); + zOut[iOut++] = 0x80 | ((v>>6)&0x3f); + zOut[iOut++] = 0x80 | (v&0x3f); + } + iIn += szEscape - 1; + }else{ + zOut[iOut++] = c; + } + } /* end for() */ + assert( iOut<=nOut ); + zOut[iOut] = 0; + sqlite3_result_text(pCtx, zOut, iOut, SQLITE_DYNAMIC); + break; + } + case JSONB_ARRAY: + case JSONB_OBJECT: { + int flags = textOnly ? 0 : SQLITE_PTR_TO_INT(sqlite3_user_data(pCtx)); + if( flags & JSON_BLOB ){ + sqlite3_result_blob(pCtx, &pParse->aBlob[i], sz+n, SQLITE_TRANSIENT); + }else{ + jsonReturnTextJsonFromBlob(pCtx, &pParse->aBlob[i], sz+n); + } + break; + } + default: { + goto returnfromblob_malformed; + } + } + return; + +returnfromblob_oom: + sqlite3_result_error_nomem(pCtx); + return; + +returnfromblob_malformed: + sqlite3_result_error(pCtx, "malformed JSON", -1); + return; +} + +/* +** pArg is a function argument that might be an SQL value or a JSON +** value. Figure out what it is and encode it as a JSONB blob. +** Return the results in pParse. +** +** pParse is uninitialized upon entry. This routine will handle the +** initialization of pParse. The result will be contained in +** pParse->aBlob and pParse->nBlob. pParse->aBlob might be dynamically +** allocated (if pParse->nBlobAlloc is greater than zero) in which case +** the caller is responsible for freeing the space allocated to pParse->aBlob +** when it has finished with it. Or pParse->aBlob might be a static string +** or a value obtained from sqlite3_value_blob(pArg). +** +** If the argument is a BLOB that is clearly not a JSONB, then this +** function might set an error message in ctx and return non-zero. +** It might also set an error message and return non-zero on an OOM error. +*/ +static int jsonFunctionArgToBlob( + sqlite3_context *ctx, + sqlite3_value *pArg, + JsonParse *pParse +){ + int eType = sqlite3_value_type(pArg); + static u8 aNull[] = { 0x00 }; + memset(pParse, 0, sizeof(pParse[0])); + pParse->db = sqlite3_context_db_handle(ctx); + switch( eType ){ + default: { + pParse->aBlob = aNull; + pParse->nBlob = 1; + return 0; + } + case SQLITE_BLOB: { + if( jsonFuncArgMightBeBinary(pArg) ){ + pParse->aBlob = (u8*)sqlite3_value_blob(pArg); + pParse->nBlob = sqlite3_value_bytes(pArg); + }else{ + sqlite3_result_error(ctx, "JSON cannot hold BLOB values", -1); + return 1; + } + break; + } + case SQLITE_TEXT: { + const char *zJson = (const char*)sqlite3_value_text(pArg); + int nJson = sqlite3_value_bytes(pArg); + if( zJson==0 ) return 1; + if( sqlite3_value_subtype(pArg)==JSON_SUBTYPE ){ + pParse->zJson = (char*)zJson; + pParse->nJson = nJson; + if( jsonConvertTextToBlob(pParse, ctx) ){ + sqlite3_result_error(ctx, "malformed JSON", -1); + sqlite3DbFree(pParse->db, pParse->aBlob); + memset(pParse, 0, sizeof(pParse[0])); + return 1; + } + }else{ + jsonBlobAppendNode(pParse, JSONB_TEXTRAW, nJson, zJson); + } + break; + } + case SQLITE_FLOAT: { + double r = sqlite3_value_double(pArg); + if( NEVER(sqlite3IsNaN(r)) ){ + jsonBlobAppendNode(pParse, JSONB_NULL, 0, 0); + }else{ + int n = sqlite3_value_bytes(pArg); + const char *z = (const char*)sqlite3_value_text(pArg); + if( z==0 ) return 1; + if( z[0]=='I' ){ + jsonBlobAppendNode(pParse, JSONB_FLOAT, 5, "9e999"); + }else if( z[0]=='-' && z[1]=='I' ){ + jsonBlobAppendNode(pParse, JSONB_FLOAT, 6, "-9e999"); + }else{ + jsonBlobAppendNode(pParse, JSONB_FLOAT, n, z); + } + } + break; + } + case SQLITE_INTEGER: { + int n = sqlite3_value_bytes(pArg); + const char *z = (const char*)sqlite3_value_text(pArg); + if( z==0 ) return 1; + jsonBlobAppendNode(pParse, JSONB_INT, n, z); + break; + } + } + if( pParse->oom ){ + sqlite3_result_error_nomem(ctx); + return 1; }else{ return 0; } - if( pParse->oom ) return 0; - return jsonLookupStep(pParse, pParse->nNode-1, zPath, pApnd, pzErr); } /* -** Return the text of a syntax error message on a JSON path. Space is -** obtained from sqlite3_malloc(). -*/ -static char *jsonPathSyntaxError(const char *zErr){ - return sqlite3_mprintf("JSON path error near '%q'", zErr); -} - -/* -** Do a node lookup using zPath. Return a pointer to the node on success. -** Return NULL if not found or if there is an error. +** Generate a bad path error. ** -** On an error, write an error message into pCtx and increment the -** pParse->nErr counter. -** -** If pApnd!=NULL then try to append missing nodes and set *pApnd = 1 if -** nodes are appended. +** If ctx is not NULL then push the error message into ctx and return NULL. +** If ctx is NULL, then return the text of the error message. */ -static JsonNode *jsonLookup( - JsonParse *pParse, /* The JSON to search */ - const char *zPath, /* The path to search */ - int *pApnd, /* Append nodes to complete path if not NULL */ - sqlite3_context *pCtx /* Report errors here, if not NULL */ +static char *jsonBadPathError( + sqlite3_context *ctx, /* The function call containing the error */ + const char *zPath /* The path with the problem */ ){ - const char *zErr = 0; - JsonNode *pNode = 0; - char *zMsg; - - if( zPath==0 ) return 0; - if( zPath[0]!='$' ){ - zErr = zPath; - goto lookup_err; - } - zPath++; - pNode = jsonLookupStep(pParse, 0, zPath, pApnd, &zErr); - if( zErr==0 ) return pNode; - -lookup_err: - pParse->nErr++; - assert( zErr!=0 && pCtx!=0 ); - zMsg = jsonPathSyntaxError(zErr); + char *zMsg = sqlite3_mprintf("bad JSON path: %Q", zPath); + if( ctx==0 ) return zMsg; if( zMsg ){ - sqlite3_result_error(pCtx, zMsg, -1); + sqlite3_result_error(ctx, zMsg, -1); sqlite3_free(zMsg); }else{ - sqlite3_result_error_nomem(pCtx); + sqlite3_result_error_nomem(ctx); } return 0; } - -/* -** Report the wrong number of arguments for json_insert(), json_replace() -** or json_set(). +/* argv[0] is a BLOB that seems likely to be a JSONB. Subsequent +** arguments come in parse where each pair contains a JSON path and +** content to insert or set at that patch. Do the updates +** and return the result. +** +** The specific operation is determined by eEdit, which can be one +** of JEDIT_INS, JEDIT_REPL, or JEDIT_SET. */ -static void jsonWrongNumArgs( - sqlite3_context *pCtx, - const char *zFuncName +static void jsonInsertIntoBlob( + sqlite3_context *ctx, + int argc, + sqlite3_value **argv, + int eEdit /* JEDIT_INS, JEDIT_REPL, or JEDIT_SET */ ){ - char *zMsg = sqlite3_mprintf("json_%s() needs an odd number of arguments", - zFuncName); - sqlite3_result_error(pCtx, zMsg, -1); - sqlite3_free(zMsg); + int i; + u32 rc = 0; + const char *zPath = 0; + int flgs; + JsonParse *p; + JsonParse ax; + + assert( (argc&1)==1 ); + flgs = argc==1 ? 0 : JSON_EDITABLE; + p = jsonParseFuncArg(ctx, argv[0], flgs); + if( p==0 ) return; + for(i=1; inBlob, ax.aBlob, ax.nBlob); + } + rc = 0; + }else{ + p->eEdit = eEdit; + p->nIns = ax.nBlob; + p->aIns = ax.aBlob; + p->delta = 0; + rc = jsonLookupStep(p, 0, zPath+1, 0); + } + jsonParseReset(&ax); + if( rc==JSON_LOOKUP_NOTFOUND ) continue; + if( JSON_LOOKUP_ISERROR(rc) ) goto jsonInsertIntoBlob_patherror; + } + jsonReturnParse(ctx, p); + jsonParseFree(p); + return; + +jsonInsertIntoBlob_patherror: + jsonParseFree(p); + if( rc==JSON_LOOKUP_ERROR ){ + sqlite3_result_error(ctx, "malformed JSON", -1); + }else{ + jsonBadPathError(ctx, zPath); + } + return; } /* -** Mark all NULL entries in the Object passed in as JNODE_REMOVE. +** If pArg is a blob that seems like a JSONB blob, then initialize +** p to point to that JSONB and return TRUE. If pArg does not seem like +** a JSONB blob, then return FALSE; +** +** This routine is only called if it is already known that pArg is a +** blob. The only open question is whether or not the blob appears +** to be a JSONB blob. */ -static void jsonRemoveAllNulls(JsonNode *pNode){ - int i, n; - assert( pNode->eType==JSON_OBJECT ); - n = pNode->n; - for(i=2; i<=n; i += jsonNodeSize(&pNode[i])+1){ - switch( pNode[i].eType ){ - case JSON_NULL: - pNode[i].jnFlags |= JNODE_REMOVE; - break; - case JSON_OBJECT: - jsonRemoveAllNulls(&pNode[i]); - break; +static int jsonArgIsJsonb(sqlite3_value *pArg, JsonParse *p){ + u32 n, sz = 0; + p->aBlob = (u8*)sqlite3_value_blob(pArg); + p->nBlob = (u32)sqlite3_value_bytes(pArg); + if( p->nBlob==0 ){ + p->aBlob = 0; + return 0; + } + if( NEVER(p->aBlob==0) ){ + return 0; + } + if( (p->aBlob[0] & 0x0f)<=JSONB_OBJECT + && (n = jsonbPayloadSize(p, 0, &sz))>0 + && sz+n==p->nBlob + && ((p->aBlob[0] & 0x0f)>JSONB_FALSE || sz==0) + ){ + return 1; + } + p->aBlob = 0; + p->nBlob = 0; + return 0; +} + +/* +** Generate a JsonParse object, containing valid JSONB in aBlob and nBlob, +** from the SQL function argument pArg. Return a pointer to the new +** JsonParse object. +** +** Ownership of the new JsonParse object is passed to the caller. The +** caller should invoke jsonParseFree() on the return value when it +** has finished using it. +** +** If any errors are detected, an appropriate error messages is set +** using sqlite3_result_error() or the equivalent and this routine +** returns NULL. This routine also returns NULL if the pArg argument +** is an SQL NULL value, but no error message is set in that case. This +** is so that SQL functions that are given NULL arguments will return +** a NULL value. +*/ +static JsonParse *jsonParseFuncArg( + sqlite3_context *ctx, + sqlite3_value *pArg, + u32 flgs +){ + int eType; /* Datatype of pArg */ + JsonParse *p = 0; /* Value to be returned */ + JsonParse *pFromCache = 0; /* Value taken from cache */ + sqlite3 *db; /* The database connection */ + + assert( ctx!=0 ); + eType = sqlite3_value_type(pArg); + if( eType==SQLITE_NULL ){ + return 0; + } + pFromCache = jsonCacheSearch(ctx, pArg); + if( pFromCache ){ + pFromCache->nJPRef++; + if( (flgs & JSON_EDITABLE)==0 ){ + return pFromCache; } } + db = sqlite3_context_db_handle(ctx); +rebuild_from_cache: + p = sqlite3DbMallocZero(db, sizeof(*p)); + if( p==0 ) goto json_pfa_oom; + memset(p, 0, sizeof(*p)); + p->db = db; + p->nJPRef = 1; + if( pFromCache!=0 ){ + u32 nBlob = pFromCache->nBlob; + p->aBlob = sqlite3DbMallocRaw(db, nBlob); + if( p->aBlob==0 ) goto json_pfa_oom; + memcpy(p->aBlob, pFromCache->aBlob, nBlob); + p->nBlobAlloc = p->nBlob = nBlob; + p->hasNonstd = pFromCache->hasNonstd; + jsonParseFree(pFromCache); + return p; + } + if( eType==SQLITE_BLOB ){ + if( jsonArgIsJsonb(pArg,p) ){ + if( (flgs & JSON_EDITABLE)!=0 && jsonBlobMakeEditable(p, 0)==0 ){ + goto json_pfa_oom; + } + return p; + } + /* If the blob is not valid JSONB, fall through into trying to cast + ** the blob into text which is then interpreted as JSON. (tag-20240123-a) + ** + ** This goes against all historical documentation about how the SQLite + ** JSON functions were suppose to work. From the beginning, blob was + ** reserved for expansion and a blob value should have raised an error. + ** But it did not, due to a bug. And many applications came to depend + ** upon this buggy behavior, espeically when using the CLI and reading + ** JSON text using readfile(), which returns a blob. For this reason + ** we will continue to support the bug moving forward. + ** See for example https://sqlite.org/forum/forumpost/012136abd5292b8d + */ + } + p->zJson = (char*)sqlite3_value_text(pArg); + p->nJson = sqlite3_value_bytes(pArg); + if( p->nJson==0 ) goto json_pfa_malformed; + if( NEVER(p->zJson==0) ) goto json_pfa_oom; + if( jsonConvertTextToBlob(p, (flgs & JSON_KEEPERROR) ? 0 : ctx) ){ + if( flgs & JSON_KEEPERROR ){ + p->nErr = 1; + return p; + }else{ + jsonParseFree(p); + return 0; + } + }else{ + int isRCStr = sqlite3ValueIsOfClass(pArg, sqlite3RCStrUnref); + int rc; + if( !isRCStr ){ + char *zNew = sqlite3RCStrNew( p->nJson ); + if( zNew==0 ) goto json_pfa_oom; + memcpy(zNew, p->zJson, p->nJson); + p->zJson = zNew; + p->zJson[p->nJson] = 0; + }else{ + sqlite3RCStrRef(p->zJson); + } + p->bJsonIsRCStr = 1; + rc = jsonCacheInsert(ctx, p); + if( rc==SQLITE_NOMEM ) goto json_pfa_oom; + if( flgs & JSON_EDITABLE ){ + pFromCache = p; + p = 0; + goto rebuild_from_cache; + } + } + return p; + +json_pfa_malformed: + if( flgs & JSON_KEEPERROR ){ + p->nErr = 1; + return p; + }else{ + jsonParseFree(p); + sqlite3_result_error(ctx, "malformed JSON", -1); + return 0; + } + +json_pfa_oom: + jsonParseFree(pFromCache); + jsonParseFree(p); + sqlite3_result_error_nomem(ctx); + return 0; } +/* +** Make the return value of a JSON function either the raw JSONB blob +** or make it JSON text, depending on whether the JSON_BLOB flag is +** set on the function. +*/ +static void jsonReturnParse( + sqlite3_context *ctx, + JsonParse *p +){ + int flgs; + if( p->oom ){ + sqlite3_result_error_nomem(ctx); + return; + } + flgs = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx)); + if( flgs & JSON_BLOB ){ + if( p->nBlobAlloc>0 && !p->bReadOnly ){ + sqlite3_result_blob(ctx, p->aBlob, p->nBlob, SQLITE_DYNAMIC); + p->nBlobAlloc = 0; + }else{ + sqlite3_result_blob(ctx, p->aBlob, p->nBlob, SQLITE_TRANSIENT); + } + }else{ + JsonString s; + jsonStringInit(&s, ctx); + p->delta = 0; + jsonTranslateBlobToText(p, 0, &s); + jsonReturnString(&s, p, ctx); + sqlite3_result_subtype(ctx, JSON_SUBTYPE); + } +} /**************************************************************************** ** SQL functions used for testing and debugging @@ -203539,63 +206596,124 @@ static void jsonRemoveAllNulls(JsonNode *pNode){ #if SQLITE_DEBUG /* -** Print N node entries. +** Decode JSONB bytes in aBlob[] starting at iStart through but not +** including iEnd. Indent the +** content by nIndent spaces. */ -static void jsonDebugPrintNodeEntries( - JsonNode *aNode, /* First node entry to print */ - int N /* Number of node entries to print */ +static void jsonDebugPrintBlob( + JsonParse *pParse, /* JSON content */ + u32 iStart, /* Start rendering here */ + u32 iEnd, /* Do not render this byte or any byte after this one */ + int nIndent, /* Indent by this many spaces */ + sqlite3_str *pOut /* Generate output into this sqlite3_str object */ ){ - int i; - for(i=0; iaBlob[iStart] & 0x0f; + u32 savedNBlob = pParse->nBlob; + sqlite3_str_appendf(pOut, "%5d:%*s", iStart, nIndent, ""); + if( pParse->nBlobAlloc>pParse->nBlob ){ + pParse->nBlob = pParse->nBlobAlloc; } - printf("node %4u: %-7s n=%-5d", i, zType, aNode[i].n); - if( (aNode[i].jnFlags & ~JNODE_LABEL)!=0 ){ - u8 f = aNode[i].jnFlags; - if( f & JNODE_RAW ) printf(" RAW"); - if( f & JNODE_ESCAPE ) printf(" ESCAPE"); - if( f & JNODE_REMOVE ) printf(" REMOVE"); - if( f & JNODE_REPLACE ) printf(" REPLACE"); - if( f & JNODE_APPEND ) printf(" APPEND"); - if( f & JNODE_JSON5 ) printf(" JSON5"); + nn = n = jsonbPayloadSize(pParse, iStart, &sz); + if( nn==0 ) nn = 1; + if( sz>0 && xaBlob[iStart+i]); } + if( n==0 ){ + sqlite3_str_appendf(pOut, " ERROR invalid node size\n"); + iStart = n==0 ? iStart+1 : iEnd; + continue; + } + pParse->nBlob = savedNBlob; + if( iStart+n+sz>iEnd ){ + iEnd = iStart+n+sz; + if( iEnd>pParse->nBlob ){ + if( pParse->nBlobAlloc>0 && iEnd>pParse->nBlobAlloc ){ + iEnd = pParse->nBlobAlloc; + }else{ + iEnd = pParse->nBlob; + } + } + } + sqlite3_str_appendall(pOut," <-- "); + switch( x ){ + case JSONB_NULL: sqlite3_str_appendall(pOut,"null"); break; + case JSONB_TRUE: sqlite3_str_appendall(pOut,"true"); break; + case JSONB_FALSE: sqlite3_str_appendall(pOut,"false"); break; + case JSONB_INT: sqlite3_str_appendall(pOut,"int"); break; + case JSONB_INT5: sqlite3_str_appendall(pOut,"int5"); break; + case JSONB_FLOAT: sqlite3_str_appendall(pOut,"float"); break; + case JSONB_FLOAT5: sqlite3_str_appendall(pOut,"float5"); break; + case JSONB_TEXT: sqlite3_str_appendall(pOut,"text"); break; + case JSONB_TEXTJ: sqlite3_str_appendall(pOut,"textj"); break; + case JSONB_TEXT5: sqlite3_str_appendall(pOut,"text5"); break; + case JSONB_TEXTRAW: sqlite3_str_appendall(pOut,"textraw"); break; + case JSONB_ARRAY: { + sqlite3_str_appendf(pOut,"array, %u bytes\n", sz); + jsonDebugPrintBlob(pParse, iStart+n, iStart+n+sz, nIndent+2, pOut); + showContent = 0; + break; + } + case JSONB_OBJECT: { + sqlite3_str_appendf(pOut, "object, %u bytes\n", sz); + jsonDebugPrintBlob(pParse, iStart+n, iStart+n+sz, nIndent+2, pOut); + showContent = 0; + break; + } + default: { + sqlite3_str_appendall(pOut, "ERROR: unknown node type\n"); + showContent = 0; + break; + } + } + if( showContent ){ + if( sz==0 && x<=JSONB_FALSE ){ + sqlite3_str_append(pOut, "\n", 1); + }else{ + u32 i; + sqlite3_str_appendall(pOut, ": \""); + for(i=iStart+n; iaBlob[i]; + if( c<0x20 || c>=0x7f ) c = '.'; + sqlite3_str_append(pOut, (char*)&c, 1); + } + sqlite3_str_append(pOut, "\"\n", 2); + } + } + iStart += n + sz; } } +static void jsonShowParse(JsonParse *pParse){ + sqlite3_str out; + char zBuf[1000]; + if( pParse==0 ){ + printf("NULL pointer\n"); + return; + }else{ + printf("nBlobAlloc = %u\n", pParse->nBlobAlloc); + printf("nBlob = %u\n", pParse->nBlob); + printf("delta = %d\n", pParse->delta); + if( pParse->nBlob==0 ) return; + printf("content (bytes 0..%u):\n", pParse->nBlob-1); + } + sqlite3StrAccumInit(&out, 0, zBuf, sizeof(zBuf), 1000000); + jsonDebugPrintBlob(pParse, 0, pParse->nBlob, 0, &out); + printf("%s", sqlite3_str_value(&out)); + sqlite3_str_reset(&out); +} #endif /* SQLITE_DEBUG */ - -#if 0 /* 1 for debugging. 0 normally. Requires -DSQLITE_DEBUG too */ -static void jsonDebugPrintParse(JsonParse *p){ - jsonDebugPrintNodeEntries(p->aNode, p->nNode); -} -static void jsonDebugPrintNode(JsonNode *pNode){ - jsonDebugPrintNodeEntries(pNode, jsonNodeSize(pNode)); -} -#else - /* The usual case */ -# define jsonDebugPrintNode(X) -# define jsonDebugPrintParse(X) -#endif - #ifdef SQLITE_DEBUG /* ** SQL function: json_parse(JSON) ** -** Parse JSON using jsonParseCached(). Then print a dump of that -** parse on standard output. Return the mimified JSON result, just -** like the json() function. +** Parse JSON using jsonParseFuncArg(). Return text that is a +** human-readable dump of the binary JSONB for the input parameter. */ static void jsonParseFunc( sqlite3_context *ctx, @@ -203603,38 +206721,19 @@ static void jsonParseFunc( sqlite3_value **argv ){ JsonParse *p; /* The parse */ + sqlite3_str out; - assert( argc==1 ); - p = jsonParseCached(ctx, argv[0], ctx, 0); + assert( argc>=1 ); + sqlite3StrAccumInit(&out, 0, 0, 0, 1000000); + p = jsonParseFuncArg(ctx, argv[0], 0); if( p==0 ) return; - printf("nNode = %u\n", p->nNode); - printf("nAlloc = %u\n", p->nAlloc); - printf("nJson = %d\n", p->nJson); - printf("nAlt = %d\n", p->nAlt); - printf("nErr = %u\n", p->nErr); - printf("oom = %u\n", p->oom); - printf("hasNonstd = %u\n", p->hasNonstd); - printf("useMod = %u\n", p->useMod); - printf("hasMod = %u\n", p->hasMod); - printf("nJPRef = %u\n", p->nJPRef); - printf("iSubst = %u\n", p->iSubst); - printf("iHold = %u\n", p->iHold); - jsonDebugPrintNodeEntries(p->aNode, p->nNode); - jsonReturnJson(p, p->aNode, ctx, 1); -} - -/* -** The json_test1(JSON) function return true (1) if the input is JSON -** text generated by another json function. It returns (0) if the input -** is not known to be JSON. -*/ -static void jsonTest1Func( - sqlite3_context *ctx, - int argc, - sqlite3_value **argv -){ - UNUSED_PARAMETER(argc); - sqlite3_result_int(ctx, sqlite3_value_subtype(argv[0])==JSON_SUBTYPE); + if( argc==1 ){ + jsonDebugPrintBlob(p, 0, p->nBlob, 0, &out); + sqlite3_result_text64(ctx, out.zText, out.nChar, SQLITE_DYNAMIC, SQLITE_UTF8); + }else{ + jsonShowParse(p); + } + jsonParseFree(p); } #endif /* SQLITE_DEBUG */ @@ -203643,7 +206742,7 @@ static void jsonTest1Func( ****************************************************************************/ /* -** Implementation of the json_QUOTE(VALUE) function. Return a JSON value +** Implementation of the json_quote(VALUE) function. Return a JSON value ** corresponding to the SQL value input. Mostly this means putting ** double-quotes around strings and returning the unquoted string "null" ** when given a NULL input. @@ -203656,9 +206755,9 @@ static void jsonQuoteFunc( JsonString jx; UNUSED_PARAMETER(argc); - jsonInit(&jx, ctx); - jsonAppendValue(&jx, argv[0]); - jsonResult(&jx); + jsonStringInit(&jx, ctx); + jsonAppendSqlValue(&jx, argv[0]); + jsonReturnString(&jx, 0, 0); sqlite3_result_subtype(ctx, JSON_SUBTYPE); } @@ -203675,18 +206774,17 @@ static void jsonArrayFunc( int i; JsonString jx; - jsonInit(&jx, ctx); + jsonStringInit(&jx, ctx); jsonAppendChar(&jx, '['); for(i=0; inNode ); if( argc==2 ){ const char *zPath = (const char*)sqlite3_value_text(argv[1]); - pNode = jsonLookup(p, zPath, 0, ctx); - }else{ - pNode = p->aNode; - } - if( pNode==0 ){ - return; - } - if( pNode->eType==JSON_ARRAY ){ - while( 1 /*exit-by-break*/ ){ - i = 1; - while( i<=pNode->n ){ - if( (pNode[i].jnFlags & JNODE_REMOVE)==0 ) n++; - i += jsonNodeSize(&pNode[i]); - } - if( (pNode->jnFlags & JNODE_APPEND)==0 ) break; - if( p->useMod==0 ) break; - assert( pNode->eU==2 ); - pNode = &p->aNode[pNode->u.iAppend]; + if( zPath==0 ){ + jsonParseFree(p); + return; } + i = jsonLookupStep(p, 0, zPath[0]=='$' ? zPath+1 : "@", 0); + if( JSON_LOOKUP_ISERROR(i) ){ + if( i==JSON_LOOKUP_NOTFOUND ){ + /* no-op */ + }else if( i==JSON_LOOKUP_PATHERROR ){ + jsonBadPathError(ctx, zPath); + }else{ + sqlite3_result_error(ctx, "malformed JSON", -1); + } + eErr = 1; + i = 0; + } + }else{ + i = 0; } - sqlite3_result_int64(ctx, n); + if( (p->aBlob[i] & 0x0f)==JSONB_ARRAY ){ + cnt = jsonbArrayCount(p, i); + } + if( !eErr ) sqlite3_result_int64(ctx, cnt); + jsonParseFree(p); } -/* -** Bit values for the flags passed into jsonExtractFunc() or -** jsonSetFunc() via the user-data value. -*/ -#define JSON_JSON 0x01 /* Result is always JSON */ -#define JSON_SQL 0x02 /* Result is always SQL */ -#define JSON_ABPATH 0x03 /* Allow abbreviated JSON path specs */ -#define JSON_ISSET 0x04 /* json_set(), not json_insert() */ +/* True if the string is all digits */ +static int jsonAllDigits(const char *z, int n){ + int i; + for(i=0; i and ->> operators accept abbreviated PATH arguments. This - ** is mostly for compatibility with PostgreSQL, but also for - ** convenience. - ** - ** NUMBER ==> $[NUMBER] // PG compatible - ** LABEL ==> $.LABEL // PG compatible - ** [NUMBER] ==> $[NUMBER] // Not PG. Purely for convenience - */ - jsonInit(&jx, ctx); - if( sqlite3Isdigit(zPath[0]) ){ - jsonAppendRawNZ(&jx, "$[", 2); - jsonAppendRaw(&jx, zPath, (int)strlen(zPath)); - jsonAppendRawNZ(&jx, "]", 2); - }else{ - jsonAppendRawNZ(&jx, "$.", 1 + (zPath[0]!='[')); - jsonAppendRaw(&jx, zPath, (int)strlen(zPath)); - jsonAppendChar(&jx, 0); - } - pNode = jx.bErr ? 0 : jsonLookup(p, jx.zBuf, 0, ctx); - jsonReset(&jx); - }else{ - pNode = jsonLookup(p, zPath, 0, ctx); - } - if( pNode ){ - if( flags & JSON_JSON ){ - jsonReturnJson(p, pNode, ctx, 0); - }else{ - jsonReturn(p, pNode, ctx); - sqlite3_result_subtype(ctx, 0); - } - } - }else{ - pNode = jsonLookup(p, zPath, 0, ctx); - if( p->nErr==0 && pNode ) jsonReturn(p, pNode, ctx); - } - }else{ - /* Two or more PATH arguments results in a JSON array with each - ** element of the array being the value selected by one of the PATHs */ - int i; - jsonInit(&jx, ctx); + flags = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx)); + jsonStringInit(&jx, ctx); + if( argc>2 ){ jsonAppendChar(&jx, '['); - for(i=1; inErr ) break; - jsonAppendSeparator(&jx); - if( pNode ){ - jsonRenderNode(p, pNode, &jx); + } + for(i=1; i and ->> operators accept abbreviated PATH arguments. This + ** is mostly for compatibility with PostgreSQL, but also for + ** convenience. + ** + ** NUMBER ==> $[NUMBER] // PG compatible + ** LABEL ==> $.LABEL // PG compatible + ** [NUMBER] ==> $[NUMBER] // Not PG. Purely for convenience + */ + jsonStringInit(&jx, ctx); + if( jsonAllDigits(zPath, nPath) ){ + jsonAppendRawNZ(&jx, "[", 1); + jsonAppendRaw(&jx, zPath, nPath); + jsonAppendRawNZ(&jx, "]", 2); + }else if( jsonAllAlphanum(zPath, nPath) ){ + jsonAppendRawNZ(&jx, ".", 1); + jsonAppendRaw(&jx, zPath, nPath); + }else if( zPath[0]=='[' && nPath>=3 && zPath[nPath-1]==']' ){ + jsonAppendRaw(&jx, zPath, nPath); }else{ + jsonAppendRawNZ(&jx, ".\"", 2); + jsonAppendRaw(&jx, zPath, nPath); + jsonAppendRawNZ(&jx, "\"", 1); + } + jsonStringTerminate(&jx); + j = jsonLookupStep(p, 0, jx.zBuf, 0); + jsonStringReset(&jx); + }else{ + jsonBadPathError(ctx, zPath); + goto json_extract_error; + } + if( jnBlob ){ + if( argc==2 ){ + if( flags & JSON_JSON ){ + jsonStringInit(&jx, ctx); + jsonTranslateBlobToText(p, j, &jx); + jsonReturnString(&jx, 0, 0); + jsonStringReset(&jx); + assert( (flags & JSON_BLOB)==0 ); + sqlite3_result_subtype(ctx, JSON_SUBTYPE); + }else{ + jsonReturnFromBlob(p, j, ctx, 0); + if( (flags & (JSON_SQL|JSON_BLOB))==0 + && (p->aBlob[j]&0x0f)>=JSONB_ARRAY + ){ + sqlite3_result_subtype(ctx, JSON_SUBTYPE); + } + } + }else{ + jsonAppendSeparator(&jx); + jsonTranslateBlobToText(p, j, &jx); + } + }else if( j==JSON_LOOKUP_NOTFOUND ){ + if( argc==2 ){ + goto json_extract_error; /* Return NULL if not found */ + }else{ + jsonAppendSeparator(&jx); jsonAppendRawNZ(&jx, "null", 4); } + }else if( j==JSON_LOOKUP_ERROR ){ + sqlite3_result_error(ctx, "malformed JSON", -1); + goto json_extract_error; + }else{ + jsonBadPathError(ctx, zPath); + goto json_extract_error; } - if( i==argc ){ - jsonAppendChar(&jx, ']'); - jsonResult(&jx); + } + if( argc>2 ){ + jsonAppendChar(&jx, ']'); + jsonReturnString(&jx, 0, 0); + if( (flags & JSON_BLOB)==0 ){ sqlite3_result_subtype(ctx, JSON_SUBTYPE); } - jsonReset(&jx); } +json_extract_error: + jsonStringReset(&jx); + jsonParseFree(p); + return; } -/* This is the RFC 7396 MergePatch algorithm. +/* +** Return codes for jsonMergePatch() */ -static JsonNode *jsonMergePatch( - JsonParse *pParse, /* The JSON parser that contains the TARGET */ - u32 iTarget, /* Node of the TARGET in pParse */ - JsonNode *pPatch /* The PATCH */ +#define JSON_MERGE_OK 0 /* Success */ +#define JSON_MERGE_BADTARGET 1 /* Malformed TARGET blob */ +#define JSON_MERGE_BADPATCH 2 /* Malformed PATCH blob */ +#define JSON_MERGE_OOM 3 /* Out-of-memory condition */ + +/* +** RFC-7396 MergePatch for two JSONB blobs. +** +** pTarget is the target. pPatch is the patch. The target is updated +** in place. The patch is read-only. +** +** The original RFC-7396 algorithm is this: +** +** define MergePatch(Target, Patch): +** if Patch is an Object: +** if Target is not an Object: +** Target = {} # Ignore the contents and set it to an empty Object +** for each Name/Value pair in Patch: +** if Value is null: +** if Name exists in Target: +** remove the Name/Value pair from Target +** else: +** Target[Name] = MergePatch(Target[Name], Value) +** return Target +** else: +** return Patch +** +** Here is an equivalent algorithm restructured to show the actual +** implementation: +** +** 01 define MergePatch(Target, Patch): +** 02 if Patch is not an Object: +** 03 return Patch +** 04 else: // if Patch is an Object +** 05 if Target is not an Object: +** 06 Target = {} +** 07 for each Name/Value pair in Patch: +** 08 if Name exists in Target: +** 09 if Value is null: +** 10 remove the Name/Value pair from Target +** 11 else +** 12 Target[name] = MergePatch(Target[Name], Value) +** 13 else if Value is not NULL: +** 14 if Value is not an Object: +** 15 Target[name] = Value +** 16 else: +** 17 Target[name] = MergePatch('{}',value) +** 18 return Target +** | +** ^---- Line numbers referenced in comments in the implementation +*/ +static int jsonMergePatch( + JsonParse *pTarget, /* The JSON parser that contains the TARGET */ + u32 iTarget, /* Index of TARGET in pTarget->aBlob[] */ + const JsonParse *pPatch, /* The PATCH */ + u32 iPatch /* Index of PATCH in pPatch->aBlob[] */ ){ - u32 i, j; - u32 iRoot; - JsonNode *pTarget; - if( pPatch->eType!=JSON_OBJECT ){ - return pPatch; + u8 x; /* Type of a single node */ + u32 n, sz=0; /* Return values from jsonbPayloadSize() */ + u32 iTCursor; /* Cursor position while scanning the target object */ + u32 iTStart; /* First label in the target object */ + u32 iTEndBE; /* Original first byte past end of target, before edit */ + u32 iTEnd; /* Current first byte past end of target */ + u8 eTLabel; /* Node type of the target label */ + u32 iTLabel = 0; /* Index of the label */ + u32 nTLabel = 0; /* Header size in bytes for the target label */ + u32 szTLabel = 0; /* Size of the target label payload */ + u32 iTValue = 0; /* Index of the target value */ + u32 nTValue = 0; /* Header size of the target value */ + u32 szTValue = 0; /* Payload size for the target value */ + + u32 iPCursor; /* Cursor position while scanning the patch */ + u32 iPEnd; /* First byte past the end of the patch */ + u8 ePLabel; /* Node type of the patch label */ + u32 iPLabel; /* Start of patch label */ + u32 nPLabel; /* Size of header on the patch label */ + u32 szPLabel; /* Payload size of the patch label */ + u32 iPValue; /* Start of patch value */ + u32 nPValue; /* Header size for the patch value */ + u32 szPValue; /* Payload size of the patch value */ + + assert( iTarget>=0 && iTargetnBlob ); + assert( iPatch>=0 && iPatchnBlob ); + x = pPatch->aBlob[iPatch] & 0x0f; + if( x!=JSONB_OBJECT ){ /* Algorithm line 02 */ + u32 szPatch; /* Total size of the patch, header+payload */ + u32 szTarget; /* Total size of the target, header+payload */ + n = jsonbPayloadSize(pPatch, iPatch, &sz); + szPatch = n+sz; + sz = 0; + n = jsonbPayloadSize(pTarget, iTarget, &sz); + szTarget = n+sz; + jsonBlobEdit(pTarget, iTarget, szTarget, pPatch->aBlob+iPatch, szPatch); + return pTarget->oom ? JSON_MERGE_OOM : JSON_MERGE_OK; /* Line 03 */ } - assert( iTargetnNode ); - pTarget = &pParse->aNode[iTarget]; - assert( (pPatch->jnFlags & JNODE_APPEND)==0 ); - if( pTarget->eType!=JSON_OBJECT ){ - jsonRemoveAllNulls(pPatch); - return pPatch; + x = pTarget->aBlob[iTarget] & 0x0f; + if( x!=JSONB_OBJECT ){ /* Algorithm line 05 */ + n = jsonbPayloadSize(pTarget, iTarget, &sz); + jsonBlobEdit(pTarget, iTarget+n, sz, 0, 0); + x = pTarget->aBlob[iTarget]; + pTarget->aBlob[iTarget] = (x & 0xf0) | JSONB_OBJECT; } - iRoot = iTarget; - for(i=1; in; i += jsonNodeSize(&pPatch[i+1])+1){ - u32 nKey; - const char *zKey; - assert( pPatch[i].eType==JSON_STRING ); - assert( pPatch[i].jnFlags & JNODE_LABEL ); - assert( pPatch[i].eU==1 ); - nKey = pPatch[i].n; - zKey = pPatch[i].u.zJContent; - for(j=1; jn; j += jsonNodeSize(&pTarget[j+1])+1 ){ - assert( pTarget[j].eType==JSON_STRING ); - assert( pTarget[j].jnFlags & JNODE_LABEL ); - if( jsonSameLabel(&pPatch[i], &pTarget[j]) ){ - if( pTarget[j+1].jnFlags & (JNODE_REMOVE|JNODE_REPLACE) ) break; - if( pPatch[i+1].eType==JSON_NULL ){ - pTarget[j+1].jnFlags |= JNODE_REMOVE; - }else{ - JsonNode *pNew = jsonMergePatch(pParse, iTarget+j+1, &pPatch[i+1]); - if( pNew==0 ) return 0; - if( pNew!=&pParse->aNode[iTarget+j+1] ){ - jsonParseAddSubstNode(pParse, iTarget+j+1); - jsonParseAddNodeArray(pParse, pNew, jsonNodeSize(pNew)); - } - pTarget = &pParse->aNode[iTarget]; - } - break; + n = jsonbPayloadSize(pPatch, iPatch, &sz); + if( NEVER(n==0) ) return JSON_MERGE_BADPATCH; + iPCursor = iPatch+n; + iPEnd = iPCursor+sz; + n = jsonbPayloadSize(pTarget, iTarget, &sz); + if( NEVER(n==0) ) return JSON_MERGE_BADTARGET; + iTStart = iTarget+n; + iTEndBE = iTStart+sz; + + while( iPCursoraBlob[iPCursor] & 0x0f; + if( ePLabelJSONB_TEXTRAW ){ + return JSON_MERGE_BADPATCH; + } + nPLabel = jsonbPayloadSize(pPatch, iPCursor, &szPLabel); + if( nPLabel==0 ) return JSON_MERGE_BADPATCH; + iPValue = iPCursor + nPLabel + szPLabel; + if( iPValue>=iPEnd ) return JSON_MERGE_BADPATCH; + nPValue = jsonbPayloadSize(pPatch, iPValue, &szPValue); + if( nPValue==0 ) return JSON_MERGE_BADPATCH; + iPCursor = iPValue + nPValue + szPValue; + if( iPCursor>iPEnd ) return JSON_MERGE_BADPATCH; + + iTCursor = iTStart; + iTEnd = iTEndBE + pTarget->delta; + while( iTCursoraBlob[iTCursor] & 0x0f; + if( eTLabelJSONB_TEXTRAW ){ + return JSON_MERGE_BADTARGET; + } + nTLabel = jsonbPayloadSize(pTarget, iTCursor, &szTLabel); + if( nTLabel==0 ) return JSON_MERGE_BADTARGET; + iTValue = iTLabel + nTLabel + szTLabel; + if( iTValue>=iTEnd ) return JSON_MERGE_BADTARGET; + nTValue = jsonbPayloadSize(pTarget, iTValue, &szTValue); + if( nTValue==0 ) return JSON_MERGE_BADTARGET; + if( iTValue + nTValue + szTValue > iTEnd ) return JSON_MERGE_BADTARGET; + isEqual = jsonLabelCompare( + (const char*)&pPatch->aBlob[iPLabel+nPLabel], + szPLabel, + (ePLabel==JSONB_TEXT || ePLabel==JSONB_TEXTRAW), + (const char*)&pTarget->aBlob[iTLabel+nTLabel], + szTLabel, + (eTLabel==JSONB_TEXT || eTLabel==JSONB_TEXTRAW)); + if( isEqual ) break; + iTCursor = iTValue + nTValue + szTValue; + } + x = pPatch->aBlob[iPValue] & 0x0f; + if( iTCursoroom) ) return JSON_MERGE_OOM; + }else{ + /* Algorithm line 12 */ + int rc, savedDelta = pTarget->delta; + pTarget->delta = 0; + rc = jsonMergePatch(pTarget, iTValue, pPatch, iPValue); + if( rc ) return rc; + pTarget->delta += savedDelta; + } + }else if( x>0 ){ /* Algorithm line 13 */ + /* No match and patch value is not NULL */ + u32 szNew = szPLabel+nPLabel; + if( (pPatch->aBlob[iPValue] & 0x0f)!=JSONB_OBJECT ){ /* Line 14 */ + jsonBlobEdit(pTarget, iTEnd, 0, 0, szPValue+nPValue+szNew); + if( pTarget->oom ) return JSON_MERGE_OOM; + memcpy(&pTarget->aBlob[iTEnd], &pPatch->aBlob[iPLabel], szNew); + memcpy(&pTarget->aBlob[iTEnd+szNew], + &pPatch->aBlob[iPValue], szPValue+nPValue); + }else{ + int rc, savedDelta; + jsonBlobEdit(pTarget, iTEnd, 0, 0, szNew+1); + if( pTarget->oom ) return JSON_MERGE_OOM; + memcpy(&pTarget->aBlob[iTEnd], &pPatch->aBlob[iPLabel], szNew); + pTarget->aBlob[iTEnd+szNew] = 0x00; + savedDelta = pTarget->delta; + pTarget->delta = 0; + rc = jsonMergePatch(pTarget, iTEnd+szNew,pPatch,iPValue); + if( rc ) return rc; + pTarget->delta += savedDelta; } } - if( j>=pTarget->n && pPatch[i+1].eType!=JSON_NULL ){ - int iStart; - JsonNode *pApnd; - u32 nApnd; - iStart = jsonParseAddNode(pParse, JSON_OBJECT, 0, 0); - jsonParseAddNode(pParse, JSON_STRING, nKey, zKey); - pApnd = &pPatch[i+1]; - if( pApnd->eType==JSON_OBJECT ) jsonRemoveAllNulls(pApnd); - nApnd = jsonNodeSize(pApnd); - jsonParseAddNodeArray(pParse, pApnd, jsonNodeSize(pApnd)); - if( pParse->oom ) return 0; - pParse->aNode[iStart].n = 1+nApnd; - pParse->aNode[iRoot].jnFlags |= JNODE_APPEND; - pParse->aNode[iRoot].u.iAppend = iStart; - VVA( pParse->aNode[iRoot].eU = 2 ); - iRoot = iStart; - pTarget = &pParse->aNode[iTarget]; - } } - return pTarget; + if( pTarget->delta ) jsonAfterEditSizeAdjust(pTarget, iTarget); + return pTarget->oom ? JSON_MERGE_OOM : JSON_MERGE_OK; } + /* ** Implementation of the json_mergepatch(JSON1,JSON2) function. Return a JSON ** object that is the result of running the RFC 7396 MergePatch() algorithm @@ -203922,28 +207182,27 @@ static void jsonPatchFunc( int argc, sqlite3_value **argv ){ - JsonParse *pX; /* The JSON that is being patched */ - JsonParse *pY; /* The patch */ - JsonNode *pResult; /* The result of the merge */ + JsonParse *pTarget; /* The TARGET */ + JsonParse *pPatch; /* The PATCH */ + int rc; /* Result code */ UNUSED_PARAMETER(argc); - pX = jsonParseCached(ctx, argv[0], ctx, 1); - if( pX==0 ) return; - assert( pX->hasMod==0 ); - pX->hasMod = 1; - pY = jsonParseCached(ctx, argv[1], ctx, 1); - if( pY==0 ) return; - pX->useMod = 1; - pY->useMod = 1; - pResult = jsonMergePatch(pX, 0, pY->aNode); - assert( pResult!=0 || pX->oom ); - if( pResult && pX->oom==0 ){ - jsonDebugPrintParse(pX); - jsonDebugPrintNode(pResult); - jsonReturnJson(pX, pResult, ctx, 0); - }else{ - sqlite3_result_error_nomem(ctx); + assert( argc==2 ); + pTarget = jsonParseFuncArg(ctx, argv[0], JSON_EDITABLE); + if( pTarget==0 ) return; + pPatch = jsonParseFuncArg(ctx, argv[1], 0); + if( pPatch ){ + rc = jsonMergePatch(pTarget, 0, pPatch, 0); + if( rc==JSON_MERGE_OK ){ + jsonReturnParse(ctx, pTarget); + }else if( rc==JSON_MERGE_OOM ){ + sqlite3_result_error_nomem(ctx); + }else{ + sqlite3_result_error(ctx, "malformed JSON", -1); + } + jsonParseFree(pPatch); } + jsonParseFree(pTarget); } @@ -203967,23 +207226,23 @@ static void jsonObjectFunc( "of arguments", -1); return; } - jsonInit(&jx, ctx); + jsonStringInit(&jx, ctx); jsonAppendChar(&jx, '{'); for(i=0; i1); - if( pParse==0 ) return; - for(i=1; i<(u32)argc; i++){ + p = jsonParseFuncArg(ctx, argv[0], argc>1 ? JSON_EDITABLE : 0); + if( p==0 ) return; + for(i=1; inErr ) goto remove_done; - if( pNode ){ - pNode->jnFlags |= JNODE_REMOVE; - pParse->hasMod = 1; - pParse->useMod = 1; + if( zPath==0 ){ + goto json_remove_done; } - } - if( (pParse->aNode[0].jnFlags & JNODE_REMOVE)==0 ){ - jsonReturnJson(pParse, pParse->aNode, ctx, 1); - } -remove_done: - jsonDebugPrintParse(p); -} - -/* -** Substitute the value at iNode with the pValue parameter. -*/ -static void jsonReplaceNode( - sqlite3_context *pCtx, - JsonParse *p, - int iNode, - sqlite3_value *pValue -){ - int idx = jsonParseAddSubstNode(p, iNode); - if( idx<=0 ){ - assert( p->oom ); - return; - } - switch( sqlite3_value_type(pValue) ){ - case SQLITE_NULL: { - jsonParseAddNode(p, JSON_NULL, 0, 0); - break; + if( zPath[0]!='$' ){ + goto json_remove_patherror; } - case SQLITE_FLOAT: { - char *z = sqlite3_mprintf("%!0.15g", sqlite3_value_double(pValue)); - int n; - if( z==0 ){ - p->oom = 1; - break; - } - n = sqlite3Strlen30(z); - jsonParseAddNode(p, JSON_REAL, n, z); - jsonParseAddCleanup(p, sqlite3_free, z); - break; + if( zPath[1]==0 ){ + /* json_remove(j,'$') returns NULL */ + goto json_remove_done; } - case SQLITE_INTEGER: { - char *z = sqlite3_mprintf("%lld", sqlite3_value_int64(pValue)); - int n; - if( z==0 ){ - p->oom = 1; - break; - } - n = sqlite3Strlen30(z); - jsonParseAddNode(p, JSON_INT, n, z); - jsonParseAddCleanup(p, sqlite3_free, z); - - break; - } - case SQLITE_TEXT: { - const char *z = (const char*)sqlite3_value_text(pValue); - u32 n = (u32)sqlite3_value_bytes(pValue); - if( z==0 ){ - p->oom = 1; - break; - } - if( sqlite3_value_subtype(pValue)!=JSON_SUBTYPE ){ - char *zCopy = sqlite3DbStrDup(0, z); - int k; - if( zCopy ){ - jsonParseAddCleanup(p, sqlite3_free, zCopy); - }else{ - p->oom = 1; - sqlite3_result_error_nomem(pCtx); - } - k = jsonParseAddNode(p, JSON_STRING, n, zCopy); - assert( k>0 || p->oom ); - if( p->oom==0 ) p->aNode[k].jnFlags |= JNODE_RAW; + p->eEdit = JEDIT_DEL; + p->delta = 0; + rc = jsonLookupStep(p, 0, zPath+1, 0); + if( JSON_LOOKUP_ISERROR(rc) ){ + if( rc==JSON_LOOKUP_NOTFOUND ){ + continue; /* No-op */ + }else if( rc==JSON_LOOKUP_PATHERROR ){ + jsonBadPathError(ctx, zPath); }else{ - JsonParse *pPatch = jsonParseCached(pCtx, pValue, pCtx, 1); - if( pPatch==0 ){ - p->oom = 1; - break; - } - jsonParseAddNodeArray(p, pPatch->aNode, pPatch->nNode); - /* The nodes copied out of pPatch and into p likely contain - ** u.zJContent pointers into pPatch->zJson. So preserve the - ** content of pPatch until p is destroyed. */ - assert( pPatch->nJPRef>=1 ); - pPatch->nJPRef++; - jsonParseAddCleanup(p, (void(*)(void*))jsonParseFree, pPatch); + sqlite3_result_error(ctx, "malformed JSON", -1); } - break; - } - default: { - jsonParseAddNode(p, JSON_NULL, 0, 0); - sqlite3_result_error(pCtx, "JSON cannot hold BLOB values", -1); - p->nErr++; - break; + goto json_remove_done; } } + jsonReturnParse(ctx, p); + jsonParseFree(p); + return; + +json_remove_patherror: + jsonBadPathError(ctx, zPath); + +json_remove_done: + jsonParseFree(p); + return; } /* @@ -204124,30 +207315,12 @@ static void jsonReplaceFunc( int argc, sqlite3_value **argv ){ - JsonParse *pParse; /* The parse */ - JsonNode *pNode; - const char *zPath; - u32 i; - if( argc<1 ) return; if( (argc&1)==0 ) { jsonWrongNumArgs(ctx, "replace"); return; } - pParse = jsonParseCached(ctx, argv[0], ctx, argc>1); - if( pParse==0 ) return; - for(i=1; i<(u32)argc; i+=2){ - zPath = (const char*)sqlite3_value_text(argv[i]); - pParse->useMod = 1; - pNode = jsonLookup(pParse, zPath, 0, ctx); - if( pParse->nErr ) goto replace_err; - if( pNode ){ - jsonReplaceNode(ctx, pParse, (u32)(pNode - pParse->aNode), argv[i+1]); - } - } - jsonReturnJson(pParse, pParse->aNode, ctx, 1); -replace_err: - jsonDebugPrintParse(pParse); + jsonInsertIntoBlob(ctx, argc, argv, JEDIT_REPL); } @@ -204168,39 +207341,16 @@ static void jsonSetFunc( int argc, sqlite3_value **argv ){ - JsonParse *pParse; /* The parse */ - JsonNode *pNode; - const char *zPath; - u32 i; - int bApnd; - int bIsSet = sqlite3_user_data(ctx)!=0; + + int flags = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx)); + int bIsSet = (flags&JSON_ISSET)!=0; if( argc<1 ) return; if( (argc&1)==0 ) { jsonWrongNumArgs(ctx, bIsSet ? "set" : "insert"); return; } - pParse = jsonParseCached(ctx, argv[0], ctx, argc>1); - if( pParse==0 ) return; - for(i=1; i<(u32)argc; i+=2){ - zPath = (const char*)sqlite3_value_text(argv[i]); - bApnd = 0; - pParse->useMod = 1; - pNode = jsonLookup(pParse, zPath, &bApnd, ctx); - if( pParse->oom ){ - sqlite3_result_error_nomem(ctx); - goto jsonSetDone; - }else if( pParse->nErr ){ - goto jsonSetDone; - }else if( pNode && (bApnd || bIsSet) ){ - jsonReplaceNode(ctx, pParse, (u32)(pNode - pParse->aNode), argv[i+1]); - } - } - jsonDebugPrintParse(pParse); - jsonReturnJson(pParse, pParse->aNode, ctx, 1); - -jsonSetDone: - /* no cleanup required */; + jsonInsertIntoBlob(ctx, argc, argv, bIsSet ? JEDIT_SET : JEDIT_INS); } /* @@ -204216,27 +207366,93 @@ static void jsonTypeFunc( sqlite3_value **argv ){ JsonParse *p; /* The parse */ - const char *zPath; - JsonNode *pNode; + const char *zPath = 0; + u32 i; - p = jsonParseCached(ctx, argv[0], ctx, 0); + p = jsonParseFuncArg(ctx, argv[0], 0); if( p==0 ) return; if( argc==2 ){ zPath = (const char*)sqlite3_value_text(argv[1]); - pNode = jsonLookup(p, zPath, 0, ctx); + if( zPath==0 ) goto json_type_done; + if( zPath[0]!='$' ){ + jsonBadPathError(ctx, zPath); + goto json_type_done; + } + i = jsonLookupStep(p, 0, zPath+1, 0); + if( JSON_LOOKUP_ISERROR(i) ){ + if( i==JSON_LOOKUP_NOTFOUND ){ + /* no-op */ + }else if( i==JSON_LOOKUP_PATHERROR ){ + jsonBadPathError(ctx, zPath); + }else{ + sqlite3_result_error(ctx, "malformed JSON", -1); + } + goto json_type_done; + } }else{ - pNode = p->aNode; - } - if( pNode ){ - sqlite3_result_text(ctx, jsonType[pNode->eType], -1, SQLITE_STATIC); + i = 0; } + sqlite3_result_text(ctx, jsonbType[p->aBlob[i]&0x0f], -1, SQLITE_STATIC); +json_type_done: + jsonParseFree(p); } /* ** json_valid(JSON) +** json_valid(JSON, FLAGS) ** -** Return 1 if JSON is a well-formed canonical JSON string according -** to RFC-7159. Return 0 otherwise. +** Check the JSON argument to see if it is well-formed. The FLAGS argument +** encodes the various constraints on what is meant by "well-formed": +** +** 0x01 Canonical RFC-8259 JSON text +** 0x02 JSON text with optional JSON-5 extensions +** 0x04 Superficially appears to be JSONB +** 0x08 Strictly well-formed JSONB +** +** If the FLAGS argument is omitted, it defaults to 1. Useful values for +** FLAGS include: +** +** 1 Strict canonical JSON text +** 2 JSON text perhaps with JSON-5 extensions +** 4 Superficially appears to be JSONB +** 5 Canonical JSON text or superficial JSONB +** 6 JSON-5 text or superficial JSONB +** 8 Strict JSONB +** 9 Canonical JSON text or strict JSONB +** 10 JSON-5 text or strict JSONB +** +** Other flag combinations are redundant. For example, every canonical +** JSON text is also well-formed JSON-5 text, so FLAG values 2 and 3 +** are the same. Similarly, any input that passes a strict JSONB validation +** will also pass the superficial validation so 12 through 15 are the same +** as 8 through 11 respectively. +** +** This routine runs in linear time to validate text and when doing strict +** JSONB validation. Superficial JSONB validation is constant time, +** assuming the BLOB is already in memory. The performance advantage +** of superficial JSONB validation is why that option is provided. +** Application developers can choose to do fast superficial validation or +** slower strict validation, according to their specific needs. +** +** Only the lower four bits of the FLAGS argument are currently used. +** Higher bits are reserved for future expansion. To facilitate +** compatibility, the current implementation raises an error if any bit +** in FLAGS is set other than the lower four bits. +** +** The original circa 2015 implementation of the JSON routines in +** SQLite only supported canonical RFC-8259 JSON text and the json_valid() +** function only accepted one argument. That is why the default value +** for the FLAGS argument is 1, since FLAGS=1 causes this routine to only +** recognize canonical RFC-8259 JSON text as valid. The extra FLAGS +** argument was added when the JSON routines were extended to support +** JSON5-like extensions and binary JSONB stored in BLOBs. +** +** Return Values: +** +** * Raise an error if FLAGS is outside the range of 1 to 15. +** * Return NULL if the input is NULL +** * Return 1 if the input is well-formed. +** * Return 0 if the input is not well-formed. */ static void jsonValidFunc( sqlite3_context *ctx, @@ -204244,79 +207460,128 @@ static void jsonValidFunc( sqlite3_value **argv ){ JsonParse *p; /* The parse */ - UNUSED_PARAMETER(argc); - if( sqlite3_value_type(argv[0])==SQLITE_NULL ){ + u8 flags = 1; + u8 res = 0; + if( argc==2 ){ + i64 f = sqlite3_value_int64(argv[1]); + if( f<1 || f>15 ){ + sqlite3_result_error(ctx, "FLAGS parameter to json_valid() must be" + " between 1 and 15", -1); + return; + } + flags = f & 0x0f; + } + switch( sqlite3_value_type(argv[0]) ){ + case SQLITE_NULL: { #ifdef SQLITE_LEGACY_JSON_VALID - /* Incorrect legacy behavior was to return FALSE for a NULL input */ - sqlite3_result_int(ctx, 0); + /* Incorrect legacy behavior was to return FALSE for a NULL input */ + sqlite3_result_int(ctx, 0); #endif - return; - } - p = jsonParseCached(ctx, argv[0], 0, 0); - if( p==0 || p->oom ){ - sqlite3_result_error_nomem(ctx); - sqlite3_free(p); - }else{ - sqlite3_result_int(ctx, p->nErr==0 && (p->hasNonstd==0 || p->useMod)); - if( p->nErr ) jsonParseFree(p); + return; + } + case SQLITE_BLOB: { + if( jsonFuncArgMightBeBinary(argv[0]) ){ + if( flags & 0x04 ){ + /* Superficial checking only - accomplished by the + ** jsonFuncArgMightBeBinary() call above. */ + res = 1; + }else if( flags & 0x08 ){ + /* Strict checking. Check by translating BLOB->TEXT->BLOB. If + ** no errors occur, call that a "strict check". */ + JsonParse px; + u32 iErr; + memset(&px, 0, sizeof(px)); + px.aBlob = (u8*)sqlite3_value_blob(argv[0]); + px.nBlob = sqlite3_value_bytes(argv[0]); + iErr = jsonbValidityCheck(&px, 0, px.nBlob, 1); + res = iErr==0; + } + break; + } + /* Fall through into interpreting the input as text. See note + ** above at tag-20240123-a. */ + /* no break */ deliberate_fall_through + } + default: { + JsonParse px; + if( (flags & 0x3)==0 ) break; + memset(&px, 0, sizeof(px)); + + p = jsonParseFuncArg(ctx, argv[0], JSON_KEEPERROR); + if( p ){ + if( p->oom ){ + sqlite3_result_error_nomem(ctx); + }else if( p->nErr ){ + /* no-op */ + }else if( (flags & 0x02)!=0 || p->hasNonstd==0 ){ + res = 1; + } + jsonParseFree(p); + }else{ + sqlite3_result_error_nomem(ctx); + } + break; + } } + sqlite3_result_int(ctx, res); } /* ** json_error_position(JSON) ** -** If the argument is not an interpretable JSON string, then return the 1-based -** character position at which the parser first recognized that the input -** was in error. The left-most character is 1. If the string is valid -** JSON, then return 0. +** If the argument is NULL, return NULL ** -** Note that json_valid() is only true for strictly conforming canonical JSON. -** But this routine returns zero if the input contains extension. Thus: +** If the argument is BLOB, do a full validity check and return non-zero +** if the check fails. The return value is the approximate 1-based offset +** to the byte of the element that contains the first error. ** -** (1) If the input X is strictly conforming canonical JSON: -** -** json_valid(X) returns true -** json_error_position(X) returns 0 -** -** (2) If the input X is JSON but it includes extension (such as JSON5) that -** are not part of RFC-8259: -** -** json_valid(X) returns false -** json_error_position(X) return 0 -** -** (3) If the input X cannot be interpreted as JSON even taking extensions -** into account: -** -** json_valid(X) return false -** json_error_position(X) returns 1 or more +** Otherwise interpret the argument is TEXT (even if it is numeric) and +** return the 1-based character position for where the parser first recognized +** that the input was not valid JSON, or return 0 if the input text looks +** ok. JSON-5 extensions are accepted. */ static void jsonErrorFunc( sqlite3_context *ctx, int argc, sqlite3_value **argv ){ - JsonParse *p; /* The parse */ + i64 iErrPos = 0; /* Error position to be returned */ + JsonParse s; + + assert( argc==1 ); UNUSED_PARAMETER(argc); - if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return; - p = jsonParseCached(ctx, argv[0], 0, 0); - if( p==0 || p->oom ){ - sqlite3_result_error_nomem(ctx); - sqlite3_free(p); - }else if( p->nErr==0 ){ - sqlite3_result_int(ctx, 0); + memset(&s, 0, sizeof(s)); + s.db = sqlite3_context_db_handle(ctx); + if( jsonFuncArgMightBeBinary(argv[0]) ){ + s.aBlob = (u8*)sqlite3_value_blob(argv[0]); + s.nBlob = sqlite3_value_bytes(argv[0]); + iErrPos = (i64)jsonbValidityCheck(&s, 0, s.nBlob, 1); }else{ - int n = 1; - u32 i; - const char *z = (const char*)sqlite3_value_text(argv[0]); - for(i=0; iiErr && ALWAYS(z[i]); i++){ - if( (z[i]&0xc0)!=0x80 ) n++; + s.zJson = (char*)sqlite3_value_text(argv[0]); + if( s.zJson==0 ) return; /* NULL input or OOM */ + s.nJson = sqlite3_value_bytes(argv[0]); + if( jsonConvertTextToBlob(&s,0) ){ + if( s.oom ){ + iErrPos = -1; + }else{ + /* Convert byte-offset s.iErr into a character offset */ + u32 k; + assert( s.zJson!=0 ); /* Because s.oom is false */ + for(k=0; kzBuf==0 ){ - jsonInit(pStr, ctx); + jsonStringInit(pStr, ctx); jsonAppendChar(pStr, '['); }else if( pStr->nUsed>1 ){ jsonAppendChar(pStr, ','); } pStr->pCtx = ctx; - jsonAppendValue(pStr, argv[0]); + jsonAppendSqlValue(pStr, argv[0]); } } static void jsonArrayCompute(sqlite3_context *ctx, int isFinal){ JsonString *pStr; pStr = (JsonString*)sqlite3_aggregate_context(ctx, 0); if( pStr ){ + int flags; pStr->pCtx = ctx; jsonAppendChar(pStr, ']'); - if( pStr->bErr ){ - if( pStr->bErr==1 ) sqlite3_result_error_nomem(ctx); - assert( pStr->bStatic ); + flags = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx)); + if( pStr->eErr ){ + jsonReturnString(pStr, 0, 0); + return; + }else if( flags & JSON_BLOB ){ + jsonReturnStringAsBlob(pStr); + if( isFinal ){ + if( !pStr->bStatic ) sqlite3RCStrUnref(pStr->zBuf); + }else{ + jsonStringTrimOneChar(pStr); + } + return; }else if( isFinal ){ sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed, pStr->bStatic ? SQLITE_TRANSIENT : - (void(*)(void*))sqlite3RCStrUnref); + sqlite3RCStrUnref); pStr->bStatic = 1; }else{ sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed, SQLITE_TRANSIENT); - pStr->nUsed--; + jsonStringTrimOneChar(pStr); } }else{ sqlite3_result_text(ctx, "[]", 2, SQLITE_STATIC); @@ -204441,35 +207716,46 @@ static void jsonObjectStep( pStr = (JsonString*)sqlite3_aggregate_context(ctx, sizeof(*pStr)); if( pStr ){ if( pStr->zBuf==0 ){ - jsonInit(pStr, ctx); + jsonStringInit(pStr, ctx); jsonAppendChar(pStr, '{'); }else if( pStr->nUsed>1 ){ jsonAppendChar(pStr, ','); } pStr->pCtx = ctx; z = (const char*)sqlite3_value_text(argv[0]); - n = (u32)sqlite3_value_bytes(argv[0]); + n = sqlite3Strlen30(z); jsonAppendString(pStr, z, n); jsonAppendChar(pStr, ':'); - jsonAppendValue(pStr, argv[1]); + jsonAppendSqlValue(pStr, argv[1]); } } static void jsonObjectCompute(sqlite3_context *ctx, int isFinal){ JsonString *pStr; pStr = (JsonString*)sqlite3_aggregate_context(ctx, 0); if( pStr ){ + int flags; jsonAppendChar(pStr, '}'); - if( pStr->bErr ){ - if( pStr->bErr==1 ) sqlite3_result_error_nomem(ctx); - assert( pStr->bStatic ); + pStr->pCtx = ctx; + flags = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx)); + if( pStr->eErr ){ + jsonReturnString(pStr, 0, 0); + return; + }else if( flags & JSON_BLOB ){ + jsonReturnStringAsBlob(pStr); + if( isFinal ){ + if( !pStr->bStatic ) sqlite3RCStrUnref(pStr->zBuf); + }else{ + jsonStringTrimOneChar(pStr); + } + return; }else if( isFinal ){ sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed, pStr->bStatic ? SQLITE_TRANSIENT : - (void(*)(void*))sqlite3RCStrUnref); + sqlite3RCStrUnref); pStr->bStatic = 1; }else{ sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed, SQLITE_TRANSIENT); - pStr->nUsed--; + jsonStringTrimOneChar(pStr); } }else{ sqlite3_result_text(ctx, "{}", 2, SQLITE_STATIC); @@ -204489,19 +207775,37 @@ static void jsonObjectFinal(sqlite3_context *ctx){ /**************************************************************************** ** The json_each virtual table ****************************************************************************/ +typedef struct JsonParent JsonParent; +struct JsonParent { + u32 iHead; /* Start of object or array */ + u32 iValue; /* Start of the value */ + u32 iEnd; /* First byte past the end */ + u32 nPath; /* Length of path */ + i64 iKey; /* Key for JSONB_ARRAY */ +}; + typedef struct JsonEachCursor JsonEachCursor; struct JsonEachCursor { sqlite3_vtab_cursor base; /* Base class - must be first */ u32 iRowid; /* The rowid */ - u32 iBegin; /* The first node of the scan */ - u32 i; /* Index in sParse.aNode[] of current row */ + u32 i; /* Index in sParse.aBlob[] of current row */ u32 iEnd; /* EOF when i equals or exceeds this value */ - u8 eType; /* Type of top-level element */ + u32 nRoot; /* Size of the root path in bytes */ + u8 eType; /* Type of the container for element i */ u8 bRecursive; /* True for json_tree(). False for json_each() */ - char *zJson; /* Input JSON */ - char *zRoot; /* Path by which to filter zJson */ + u32 nParent; /* Current nesting depth */ + u32 nParentAlloc; /* Space allocated for aParent[] */ + JsonParent *aParent; /* Parent elements of i */ + sqlite3 *db; /* Database connection */ + JsonString path; /* Current path */ JsonParse sParse; /* Parse of the input JSON */ }; +typedef struct JsonEachConnection JsonEachConnection; +struct JsonEachConnection { + sqlite3_vtab base; /* Base class - must be first */ + sqlite3 *db; /* Database connection */ +}; + /* Constructor for the json_each virtual table */ static int jsonEachConnect( @@ -204511,7 +207815,7 @@ static int jsonEachConnect( sqlite3_vtab **ppVtab, char **pzErr ){ - sqlite3_vtab *pNew; + JsonEachConnection *pNew; int rc; /* Column numbers */ @@ -204537,28 +207841,32 @@ static int jsonEachConnect( "CREATE TABLE x(key,value,type,atom,id,parent,fullkey,path," "json HIDDEN,root HIDDEN)"); if( rc==SQLITE_OK ){ - pNew = *ppVtab = sqlite3_malloc( sizeof(*pNew) ); + pNew = (JsonEachConnection*)sqlite3DbMallocZero(db, sizeof(*pNew)); + *ppVtab = (sqlite3_vtab*)pNew; if( pNew==0 ) return SQLITE_NOMEM; - memset(pNew, 0, sizeof(*pNew)); sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS); + pNew->db = db; } return rc; } /* destructor for json_each virtual table */ static int jsonEachDisconnect(sqlite3_vtab *pVtab){ - sqlite3_free(pVtab); + JsonEachConnection *p = (JsonEachConnection*)pVtab; + sqlite3DbFree(p->db, pVtab); return SQLITE_OK; } /* constructor for a JsonEachCursor object for json_each(). */ static int jsonEachOpenEach(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){ + JsonEachConnection *pVtab = (JsonEachConnection*)p; JsonEachCursor *pCur; UNUSED_PARAMETER(p); - pCur = sqlite3_malloc( sizeof(*pCur) ); + pCur = sqlite3DbMallocZero(pVtab->db, sizeof(*pCur)); if( pCur==0 ) return SQLITE_NOMEM; - memset(pCur, 0, sizeof(*pCur)); + pCur->db = pVtab->db; + jsonStringZero(&pCur->path); *ppCursor = &pCur->base; return SQLITE_OK; } @@ -204576,21 +207884,24 @@ static int jsonEachOpenTree(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){ /* Reset a JsonEachCursor back to its original state. Free any memory ** held. */ static void jsonEachCursorReset(JsonEachCursor *p){ - sqlite3_free(p->zRoot); jsonParseReset(&p->sParse); + jsonStringReset(&p->path); + sqlite3DbFree(p->db, p->aParent); p->iRowid = 0; p->i = 0; + p->aParent = 0; + p->nParent = 0; + p->nParentAlloc = 0; p->iEnd = 0; p->eType = 0; - p->zJson = 0; - p->zRoot = 0; } /* Destructor for a jsonEachCursor object */ static int jsonEachClose(sqlite3_vtab_cursor *cur){ JsonEachCursor *p = (JsonEachCursor*)cur; jsonEachCursorReset(p); - sqlite3_free(cur); + + sqlite3DbFree(p->db, cur); return SQLITE_OK; } @@ -204601,200 +207912,230 @@ static int jsonEachEof(sqlite3_vtab_cursor *cur){ return p->i >= p->iEnd; } -/* Advance the cursor to the next element for json_tree() */ -static int jsonEachNext(sqlite3_vtab_cursor *cur){ - JsonEachCursor *p = (JsonEachCursor*)cur; - if( p->bRecursive ){ - if( p->sParse.aNode[p->i].jnFlags & JNODE_LABEL ) p->i++; - p->i++; - p->iRowid++; - if( p->iiEnd ){ - u32 iUp = p->sParse.aUp[p->i]; - JsonNode *pUp = &p->sParse.aNode[iUp]; - p->eType = pUp->eType; - if( pUp->eType==JSON_ARRAY ){ - assert( pUp->eU==0 || pUp->eU==3 ); - testcase( pUp->eU==3 ); - VVA( pUp->eU = 3 ); - if( iUp==p->i-1 ){ - pUp->u.iKey = 0; - }else{ - pUp->u.iKey++; +/* +** If the cursor is currently pointing at the label of a object entry, +** then return the index of the value. For all other cases, return the +** current pointer position, which is the value. +*/ +static int jsonSkipLabel(JsonEachCursor *p){ + if( p->eType==JSONB_OBJECT ){ + u32 sz = 0; + u32 n = jsonbPayloadSize(&p->sParse, p->i, &sz); + return p->i + n + sz; + }else{ + return p->i; + } +} + +/* +** Append the path name for the current element. +*/ +static void jsonAppendPathName(JsonEachCursor *p){ + assert( p->nParent>0 ); + assert( p->eType==JSONB_ARRAY || p->eType==JSONB_OBJECT ); + if( p->eType==JSONB_ARRAY ){ + jsonPrintf(30, &p->path, "[%lld]", p->aParent[p->nParent-1].iKey); + }else{ + u32 n, sz = 0, k, i; + const char *z; + int needQuote = 0; + n = jsonbPayloadSize(&p->sParse, p->i, &sz); + k = p->i + n; + z = (const char*)&p->sParse.aBlob[k]; + if( sz==0 || !sqlite3Isalpha(z[0]) ){ + needQuote = 1; + }else{ + for(i=0; ipath,".\"%.*s\"", sz, z); + }else{ + jsonPrintf(sz+2,&p->path,".%.*s", sz, z); + } + } +} + +/* Advance the cursor to the next element for json_tree() */ +static int jsonEachNext(sqlite3_vtab_cursor *cur){ + JsonEachCursor *p = (JsonEachCursor*)cur; + int rc = SQLITE_OK; + if( p->bRecursive ){ + u8 x; + u8 levelChange = 0; + u32 n, sz = 0; + u32 i = jsonSkipLabel(p); + x = p->sParse.aBlob[i] & 0x0f; + n = jsonbPayloadSize(&p->sParse, i, &sz); + if( x==JSONB_OBJECT || x==JSONB_ARRAY ){ + JsonParent *pParent; + if( p->nParent>=p->nParentAlloc ){ + JsonParent *pNew; + u64 nNew; + nNew = p->nParentAlloc*2 + 3; + pNew = sqlite3DbRealloc(p->db, p->aParent, sizeof(JsonParent)*nNew); + if( pNew==0 ) return SQLITE_NOMEM; + p->nParentAlloc = (u32)nNew; + p->aParent = pNew; + } + levelChange = 1; + pParent = &p->aParent[p->nParent]; + pParent->iHead = p->i; + pParent->iValue = i; + pParent->iEnd = i + n + sz; + pParent->iKey = -1; + pParent->nPath = (u32)p->path.nUsed; + if( p->eType && p->nParent ){ + jsonAppendPathName(p); + if( p->path.eErr ) rc = SQLITE_NOMEM; + } + p->nParent++; + p->i = i + n; + }else{ + p->i = i + n + sz; + } + while( p->nParent>0 && p->i >= p->aParent[p->nParent-1].iEnd ){ + p->nParent--; + p->path.nUsed = p->aParent[p->nParent].nPath; + levelChange = 1; + } + if( levelChange ){ + if( p->nParent>0 ){ + JsonParent *pParent = &p->aParent[p->nParent-1]; + u32 iVal = pParent->iValue; + p->eType = p->sParse.aBlob[iVal] & 0x0f; + }else{ + p->eType = 0; + } + } }else{ - switch( p->eType ){ - case JSON_ARRAY: { - p->i += jsonNodeSize(&p->sParse.aNode[p->i]); - p->iRowid++; - break; - } - case JSON_OBJECT: { - p->i += 1 + jsonNodeSize(&p->sParse.aNode[p->i+1]); - p->iRowid++; - break; - } - default: { - p->i = p->iEnd; - break; + u32 n, sz = 0; + u32 i = jsonSkipLabel(p); + n = jsonbPayloadSize(&p->sParse, i, &sz); + p->i = i + n + sz; + } + if( p->eType==JSONB_ARRAY && p->nParent ){ + p->aParent[p->nParent-1].iKey++; + } + p->iRowid++; + return rc; +} + +/* Length of the path for rowid==0 in bRecursive mode. +*/ +static int jsonEachPathLength(JsonEachCursor *p){ + u32 n = p->path.nUsed; + char *z = p->path.zBuf; + if( p->iRowid==0 && p->bRecursive && n>=2 ){ + while( n>1 ){ + n--; + if( z[n]=='[' || z[n]=='.' ){ + u32 x, sz = 0; + char cSaved = z[n]; + z[n] = 0; + assert( p->sParse.eEdit==0 ); + x = jsonLookupStep(&p->sParse, 0, z+1, 0); + z[n] = cSaved; + if( JSON_LOOKUP_ISERROR(x) ) continue; + if( x + jsonbPayloadSize(&p->sParse, x, &sz) == p->i ) break; } } } - return SQLITE_OK; -} - -/* Append an object label to the JSON Path being constructed -** in pStr. -*/ -static void jsonAppendObjectPathElement( - JsonString *pStr, - JsonNode *pNode -){ - int jj, nn; - const char *z; - assert( pNode->eType==JSON_STRING ); - assert( pNode->jnFlags & JNODE_LABEL ); - assert( pNode->eU==1 ); - z = pNode->u.zJContent; - nn = pNode->n; - if( (pNode->jnFlags & JNODE_RAW)==0 ){ - assert( nn>=2 ); - assert( z[0]=='"' || z[0]=='\'' ); - assert( z[nn-1]=='"' || z[0]=='\'' ); - if( nn>2 && sqlite3Isalpha(z[1]) ){ - for(jj=2; jjsParse.aUp[i]; - jsonEachComputePath(p, pStr, iUp); - pNode = &p->sParse.aNode[i]; - pUp = &p->sParse.aNode[iUp]; - if( pUp->eType==JSON_ARRAY ){ - assert( pUp->eU==3 || (pUp->eU==0 && pUp->u.iKey==0) ); - testcase( pUp->eU==0 ); - jsonPrintf(30, pStr, "[%d]", pUp->u.iKey); - }else{ - assert( pUp->eType==JSON_OBJECT ); - if( (pNode->jnFlags & JNODE_LABEL)==0 ) pNode--; - jsonAppendObjectPathElement(pStr, pNode); - } + return n; } /* Return the value of a column */ static int jsonEachColumn( sqlite3_vtab_cursor *cur, /* The cursor */ sqlite3_context *ctx, /* First argument to sqlite3_result_...() */ - int i /* Which column to return */ + int iColumn /* Which column to return */ ){ JsonEachCursor *p = (JsonEachCursor*)cur; - JsonNode *pThis = &p->sParse.aNode[p->i]; - switch( i ){ + switch( iColumn ){ case JEACH_KEY: { - if( p->i==0 ) break; - if( p->eType==JSON_OBJECT ){ - jsonReturn(&p->sParse, pThis, ctx); - }else if( p->eType==JSON_ARRAY ){ - u32 iKey; - if( p->bRecursive ){ - if( p->iRowid==0 ) break; - assert( p->sParse.aNode[p->sParse.aUp[p->i]].eU==3 ); - iKey = p->sParse.aNode[p->sParse.aUp[p->i]].u.iKey; + if( p->nParent==0 ){ + u32 n, j; + if( p->nRoot==1 ) break; + j = jsonEachPathLength(p); + n = p->nRoot - j; + if( n==0 ){ + break; + }else if( p->path.zBuf[j]=='[' ){ + i64 x; + sqlite3Atoi64(&p->path.zBuf[j+1], &x, n-1, SQLITE_UTF8); + sqlite3_result_int64(ctx, x); + }else if( p->path.zBuf[j+1]=='"' ){ + sqlite3_result_text(ctx, &p->path.zBuf[j+2], n-3, SQLITE_TRANSIENT); }else{ - iKey = p->iRowid; + sqlite3_result_text(ctx, &p->path.zBuf[j+1], n-1, SQLITE_TRANSIENT); } - sqlite3_result_int64(ctx, (sqlite3_int64)iKey); + break; + } + if( p->eType==JSONB_OBJECT ){ + jsonReturnFromBlob(&p->sParse, p->i, ctx, 1); + }else{ + assert( p->eType==JSONB_ARRAY ); + sqlite3_result_int64(ctx, p->aParent[p->nParent-1].iKey); } break; } case JEACH_VALUE: { - if( pThis->jnFlags & JNODE_LABEL ) pThis++; - jsonReturn(&p->sParse, pThis, ctx); + u32 i = jsonSkipLabel(p); + jsonReturnFromBlob(&p->sParse, i, ctx, 1); break; } case JEACH_TYPE: { - if( pThis->jnFlags & JNODE_LABEL ) pThis++; - sqlite3_result_text(ctx, jsonType[pThis->eType], -1, SQLITE_STATIC); + u32 i = jsonSkipLabel(p); + u8 eType = p->sParse.aBlob[i] & 0x0f; + sqlite3_result_text(ctx, jsonbType[eType], -1, SQLITE_STATIC); break; } case JEACH_ATOM: { - if( pThis->jnFlags & JNODE_LABEL ) pThis++; - if( pThis->eType>=JSON_ARRAY ) break; - jsonReturn(&p->sParse, pThis, ctx); + u32 i = jsonSkipLabel(p); + if( (p->sParse.aBlob[i] & 0x0f)sParse, i, ctx, 1); + } break; } case JEACH_ID: { - sqlite3_result_int64(ctx, - (sqlite3_int64)p->i + ((pThis->jnFlags & JNODE_LABEL)!=0)); + sqlite3_result_int64(ctx, (sqlite3_int64)p->i); break; } case JEACH_PARENT: { - if( p->i>p->iBegin && p->bRecursive ){ - sqlite3_result_int64(ctx, (sqlite3_int64)p->sParse.aUp[p->i]); + if( p->nParent>0 && p->bRecursive ){ + sqlite3_result_int64(ctx, p->aParent[p->nParent-1].iHead); } break; } case JEACH_FULLKEY: { - JsonString x; - jsonInit(&x, ctx); - if( p->bRecursive ){ - jsonEachComputePath(p, &x, p->i); - }else{ - if( p->zRoot ){ - jsonAppendRaw(&x, p->zRoot, (int)strlen(p->zRoot)); - }else{ - jsonAppendChar(&x, '$'); - } - if( p->eType==JSON_ARRAY ){ - jsonPrintf(30, &x, "[%d]", p->iRowid); - }else if( p->eType==JSON_OBJECT ){ - jsonAppendObjectPathElement(&x, pThis); - } - } - jsonResult(&x); + u64 nBase = p->path.nUsed; + if( p->nParent ) jsonAppendPathName(p); + sqlite3_result_text64(ctx, p->path.zBuf, p->path.nUsed, + SQLITE_TRANSIENT, SQLITE_UTF8); + p->path.nUsed = nBase; break; } case JEACH_PATH: { - if( p->bRecursive ){ - JsonString x; - jsonInit(&x, ctx); - jsonEachComputePath(p, &x, p->sParse.aUp[p->i]); - jsonResult(&x); - break; - } - /* For json_each() path and root are the same so fall through - ** into the root case */ - /* no break */ deliberate_fall_through + u32 n = jsonEachPathLength(p); + sqlite3_result_text64(ctx, p->path.zBuf, n, + SQLITE_TRANSIENT, SQLITE_UTF8); + break; } default: { - const char *zRoot = p->zRoot; - if( zRoot==0 ) zRoot = "$"; - sqlite3_result_text(ctx, zRoot, -1, SQLITE_STATIC); + sqlite3_result_text(ctx, p->path.zBuf, p->nRoot, SQLITE_STATIC); break; } case JEACH_JSON: { - assert( i==JEACH_JSON ); - sqlite3_result_text(ctx, p->sParse.zJson, -1, SQLITE_STATIC); + if( p->sParse.zJson==0 ){ + sqlite3_result_blob(ctx, p->sParse.aBlob, p->sParse.nBlob, + SQLITE_STATIC); + }else{ + sqlite3_result_text(ctx, p->sParse.zJson, -1, SQLITE_STATIC); + } break; } } @@ -204885,86 +208226,97 @@ static int jsonEachFilter( int argc, sqlite3_value **argv ){ JsonEachCursor *p = (JsonEachCursor*)cur; - const char *z; const char *zRoot = 0; - sqlite3_int64 n; + u32 i, n, sz; UNUSED_PARAMETER(idxStr); UNUSED_PARAMETER(argc); jsonEachCursorReset(p); if( idxNum==0 ) return SQLITE_OK; - z = (const char*)sqlite3_value_text(argv[0]); - if( z==0 ) return SQLITE_OK; memset(&p->sParse, 0, sizeof(p->sParse)); p->sParse.nJPRef = 1; - if( sqlite3ValueIsOfClass(argv[0], (void(*)(void*))sqlite3RCStrUnref) ){ - p->sParse.zJson = sqlite3RCStrRef((char*)z); + p->sParse.db = p->db; + if( jsonFuncArgMightBeBinary(argv[0]) ){ + p->sParse.nBlob = sqlite3_value_bytes(argv[0]); + p->sParse.aBlob = (u8*)sqlite3_value_blob(argv[0]); }else{ - n = sqlite3_value_bytes(argv[0]); - p->sParse.zJson = sqlite3RCStrNew( n+1 ); - if( p->sParse.zJson==0 ) return SQLITE_NOMEM; - memcpy(p->sParse.zJson, z, (size_t)n+1); - } - p->sParse.bJsonIsRCStr = 1; - p->zJson = p->sParse.zJson; - if( jsonParse(&p->sParse, 0) ){ - int rc = SQLITE_NOMEM; - if( p->sParse.oom==0 ){ - sqlite3_free(cur->pVtab->zErrMsg); - cur->pVtab->zErrMsg = sqlite3_mprintf("malformed JSON"); - if( cur->pVtab->zErrMsg ) rc = SQLITE_ERROR; + p->sParse.zJson = (char*)sqlite3_value_text(argv[0]); + p->sParse.nJson = sqlite3_value_bytes(argv[0]); + if( p->sParse.zJson==0 ){ + p->i = p->iEnd = 0; + return SQLITE_OK; } - jsonEachCursorReset(p); - return rc; - }else if( p->bRecursive && jsonParseFindParents(&p->sParse) ){ - jsonEachCursorReset(p); - return SQLITE_NOMEM; - }else{ - JsonNode *pNode = 0; - if( idxNum==3 ){ - const char *zErr = 0; - zRoot = (const char*)sqlite3_value_text(argv[1]); - if( zRoot==0 ) return SQLITE_OK; - n = sqlite3_value_bytes(argv[1]); - p->zRoot = sqlite3_malloc64( n+1 ); - if( p->zRoot==0 ) return SQLITE_NOMEM; - memcpy(p->zRoot, zRoot, (size_t)n+1); - if( zRoot[0]!='$' ){ - zErr = zRoot; - }else{ - pNode = jsonLookupStep(&p->sParse, 0, p->zRoot+1, 0, &zErr); + if( jsonConvertTextToBlob(&p->sParse, 0) ){ + if( p->sParse.oom ){ + return SQLITE_NOMEM; } - if( zErr ){ + goto json_each_malformed_input; + } + } + if( idxNum==3 ){ + zRoot = (const char*)sqlite3_value_text(argv[1]); + if( zRoot==0 ) return SQLITE_OK; + if( zRoot[0]!='$' ){ + sqlite3_free(cur->pVtab->zErrMsg); + cur->pVtab->zErrMsg = jsonBadPathError(0, zRoot); + jsonEachCursorReset(p); + return cur->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM; + } + p->nRoot = sqlite3Strlen30(zRoot); + if( zRoot[1]==0 ){ + i = p->i = 0; + p->eType = 0; + }else{ + i = jsonLookupStep(&p->sParse, 0, zRoot+1, 0); + if( JSON_LOOKUP_ISERROR(i) ){ + if( i==JSON_LOOKUP_NOTFOUND ){ + p->i = 0; + p->eType = 0; + p->iEnd = 0; + return SQLITE_OK; + } sqlite3_free(cur->pVtab->zErrMsg); - cur->pVtab->zErrMsg = jsonPathSyntaxError(zErr); + cur->pVtab->zErrMsg = jsonBadPathError(0, zRoot); jsonEachCursorReset(p); return cur->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM; - }else if( pNode==0 ){ - return SQLITE_OK; } - }else{ - pNode = p->sParse.aNode; - } - p->iBegin = p->i = (int)(pNode - p->sParse.aNode); - p->eType = pNode->eType; - if( p->eType>=JSON_ARRAY ){ - assert( pNode->eU==0 ); - VVA( pNode->eU = 3 ); - pNode->u.iKey = 0; - p->iEnd = p->i + pNode->n + 1; - if( p->bRecursive ){ - p->eType = p->sParse.aNode[p->sParse.aUp[p->i]].eType; - if( p->i>0 && (p->sParse.aNode[p->i-1].jnFlags & JNODE_LABEL)!=0 ){ - p->i--; - } + if( p->sParse.iLabel ){ + p->i = p->sParse.iLabel; + p->eType = JSONB_OBJECT; }else{ - p->i++; + p->i = i; + p->eType = JSONB_ARRAY; } - }else{ - p->iEnd = p->i+1; } + jsonAppendRaw(&p->path, zRoot, p->nRoot); + }else{ + i = p->i = 0; + p->eType = 0; + p->nRoot = 1; + jsonAppendRaw(&p->path, "$", 1); + } + p->nParent = 0; + n = jsonbPayloadSize(&p->sParse, i, &sz); + p->iEnd = i+n+sz; + if( (p->sParse.aBlob[i] & 0x0f)>=JSONB_ARRAY && !p->bRecursive ){ + p->i = i + n; + p->eType = p->sParse.aBlob[i] & 0x0f; + p->aParent = sqlite3DbMallocZero(p->db, sizeof(JsonParent)); + if( p->aParent==0 ) return SQLITE_NOMEM; + p->nParent = 1; + p->nParentAlloc = 1; + p->aParent[0].iKey = 0; + p->aParent[0].iEnd = p->iEnd; + p->aParent[0].iHead = p->i; + p->aParent[0].iValue = i; } return SQLITE_OK; + +json_each_malformed_input: + sqlite3_free(cur->pVtab->zErrMsg); + cur->pVtab->zErrMsg = sqlite3_mprintf("malformed JSON"); + jsonEachCursorReset(p); + return cur->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM; } /* The methods of the json_each virtual table */ @@ -204992,7 +208344,8 @@ static sqlite3_module jsonEachModule = { 0, /* xSavepoint */ 0, /* xRelease */ 0, /* xRollbackTo */ - 0 /* xShadowName */ + 0, /* xShadowName */ + 0 /* xIntegrity */ }; /* The methods of the json_tree virtual table. */ @@ -205020,7 +208373,8 @@ static sqlite3_module jsonTreeModule = { 0, /* xSavepoint */ 0, /* xRelease */ 0, /* xRollbackTo */ - 0 /* xShadowName */ + 0, /* xShadowName */ + 0 /* xIntegrity */ }; #endif /* SQLITE_OMIT_VIRTUALTABLE */ #endif /* !defined(SQLITE_OMIT_JSON) */ @@ -205031,34 +208385,57 @@ static sqlite3_module jsonTreeModule = { SQLITE_PRIVATE void sqlite3RegisterJsonFunctions(void){ #ifndef SQLITE_OMIT_JSON static FuncDef aJsonFunc[] = { - JFUNCTION(json, 1, 0, jsonRemoveFunc), - JFUNCTION(json_array, -1, 0, jsonArrayFunc), - JFUNCTION(json_array_length, 1, 0, jsonArrayLengthFunc), - JFUNCTION(json_array_length, 2, 0, jsonArrayLengthFunc), - JFUNCTION(json_error_position,1, 0, jsonErrorFunc), - JFUNCTION(json_extract, -1, 0, jsonExtractFunc), - JFUNCTION(->, 2, JSON_JSON, jsonExtractFunc), - JFUNCTION(->>, 2, JSON_SQL, jsonExtractFunc), - JFUNCTION(json_insert, -1, 0, jsonSetFunc), - JFUNCTION(json_object, -1, 0, jsonObjectFunc), - JFUNCTION(json_patch, 2, 0, jsonPatchFunc), - JFUNCTION(json_quote, 1, 0, jsonQuoteFunc), - JFUNCTION(json_remove, -1, 0, jsonRemoveFunc), - JFUNCTION(json_replace, -1, 0, jsonReplaceFunc), - JFUNCTION(json_set, -1, JSON_ISSET, jsonSetFunc), - JFUNCTION(json_type, 1, 0, jsonTypeFunc), - JFUNCTION(json_type, 2, 0, jsonTypeFunc), - JFUNCTION(json_valid, 1, 0, jsonValidFunc), + /* sqlite3_result_subtype() ----, ,--- sqlite3_value_subtype() */ + /* | | */ + /* Uses cache ------, | | ,---- Returns JSONB */ + /* | | | | */ + /* Number of arguments ---, | | | | ,--- Flags */ + /* | | | | | | */ + JFUNCTION(json, 1,1,1, 0,0,0, jsonRemoveFunc), + JFUNCTION(jsonb, 1,1,0, 0,1,0, jsonRemoveFunc), + JFUNCTION(json_array, -1,0,1, 1,0,0, jsonArrayFunc), + JFUNCTION(jsonb_array, -1,0,1, 1,1,0, jsonArrayFunc), + JFUNCTION(json_array_length, 1,1,0, 0,0,0, jsonArrayLengthFunc), + JFUNCTION(json_array_length, 2,1,0, 0,0,0, jsonArrayLengthFunc), + JFUNCTION(json_error_position,1,1,0, 0,0,0, jsonErrorFunc), + JFUNCTION(json_extract, -1,1,1, 0,0,0, jsonExtractFunc), + JFUNCTION(jsonb_extract, -1,1,0, 0,1,0, jsonExtractFunc), + JFUNCTION(->, 2,1,1, 0,0,JSON_JSON, jsonExtractFunc), + JFUNCTION(->>, 2,1,0, 0,0,JSON_SQL, jsonExtractFunc), + JFUNCTION(json_insert, -1,1,1, 1,0,0, jsonSetFunc), + JFUNCTION(jsonb_insert, -1,1,0, 1,1,0, jsonSetFunc), + JFUNCTION(json_object, -1,0,1, 1,0,0, jsonObjectFunc), + JFUNCTION(jsonb_object, -1,0,1, 1,1,0, jsonObjectFunc), + JFUNCTION(json_patch, 2,1,1, 0,0,0, jsonPatchFunc), + JFUNCTION(jsonb_patch, 2,1,0, 0,1,0, jsonPatchFunc), + JFUNCTION(json_quote, 1,0,1, 1,0,0, jsonQuoteFunc), + JFUNCTION(json_remove, -1,1,1, 0,0,0, jsonRemoveFunc), + JFUNCTION(jsonb_remove, -1,1,0, 0,1,0, jsonRemoveFunc), + JFUNCTION(json_replace, -1,1,1, 1,0,0, jsonReplaceFunc), + JFUNCTION(jsonb_replace, -1,1,0, 1,1,0, jsonReplaceFunc), + JFUNCTION(json_set, -1,1,1, 1,0,JSON_ISSET, jsonSetFunc), + JFUNCTION(jsonb_set, -1,1,0, 1,1,JSON_ISSET, jsonSetFunc), + JFUNCTION(json_type, 1,1,0, 0,0,0, jsonTypeFunc), + JFUNCTION(json_type, 2,1,0, 0,0,0, jsonTypeFunc), + JFUNCTION(json_valid, 1,1,0, 0,0,0, jsonValidFunc), + JFUNCTION(json_valid, 2,1,0, 0,0,0, jsonValidFunc), #if SQLITE_DEBUG - JFUNCTION(json_parse, 1, 0, jsonParseFunc), - JFUNCTION(json_test1, 1, 0, jsonTest1Func), + JFUNCTION(json_parse, 1,1,0, 0,0,0, jsonParseFunc), #endif WAGGREGATE(json_group_array, 1, 0, 0, jsonArrayStep, jsonArrayFinal, jsonArrayValue, jsonGroupInverse, - SQLITE_SUBTYPE|SQLITE_UTF8|SQLITE_DETERMINISTIC), + SQLITE_SUBTYPE|SQLITE_RESULT_SUBTYPE|SQLITE_UTF8| + SQLITE_DETERMINISTIC), + WAGGREGATE(jsonb_group_array, 1, JSON_BLOB, 0, + jsonArrayStep, jsonArrayFinal, jsonArrayValue, jsonGroupInverse, + SQLITE_SUBTYPE|SQLITE_RESULT_SUBTYPE|SQLITE_UTF8|SQLITE_DETERMINISTIC), WAGGREGATE(json_group_object, 2, 0, 0, jsonObjectStep, jsonObjectFinal, jsonObjectValue, jsonGroupInverse, - SQLITE_SUBTYPE|SQLITE_UTF8|SQLITE_DETERMINISTIC) + SQLITE_SUBTYPE|SQLITE_RESULT_SUBTYPE|SQLITE_UTF8|SQLITE_DETERMINISTIC), + WAGGREGATE(jsonb_group_object,2, JSON_BLOB, 0, + jsonObjectStep, jsonObjectFinal, jsonObjectValue, jsonGroupInverse, + SQLITE_SUBTYPE|SQLITE_RESULT_SUBTYPE|SQLITE_UTF8| + SQLITE_DETERMINISTIC) }; sqlite3InsertBuiltinFuncs(aJsonFunc, ArraySize(aJsonFunc)); #endif @@ -205255,6 +208632,7 @@ struct Rtree { int iDepth; /* Current depth of the r-tree structure */ char *zDb; /* Name of database containing r-tree table */ char *zName; /* Name of r-tree table */ + char *zNodeName; /* Name of the %_node table */ u32 nBusy; /* Current number of users of this structure */ i64 nRowEst; /* Estimated number of rows in this table */ u32 nCursor; /* Number of open cursors */ @@ -205267,7 +208645,6 @@ struct Rtree { ** headed by the node (leaf nodes have RtreeNode.iNode==0). */ RtreeNode *pDeleted; - int iReinsertHeight; /* Height of sub-trees Reinsert() has run on */ /* Blob I/O on xxx_node */ sqlite3_blob *pNodeBlob; @@ -205564,15 +208941,20 @@ struct RtreeMatchArg { ** -DSQLITE_RUNTIME_BYTEORDER=1 is set, then byte-order is determined ** at run-time. */ -#ifndef SQLITE_BYTEORDER -# if defined(i386) || defined(__i386__) || defined(_M_IX86) || \ +#ifndef SQLITE_BYTEORDER /* Replicate changes at tag-20230904a */ +# if defined(__BYTE_ORDER__) && __BYTE_ORDER__==__ORDER_BIG_ENDIAN__ +# define SQLITE_BYTEORDER 4321 +# elif defined(__BYTE_ORDER__) && __BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__ +# define SQLITE_BYTEORDER 1234 +# elif defined(__BIG_ENDIAN__) && __BIG_ENDIAN__==1 +# define SQLITE_BYTEORDER 4321 +# elif defined(i386) || defined(__i386__) || defined(_M_IX86) || \ defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || \ defined(_M_AMD64) || defined(_M_ARM) || defined(__x86) || \ defined(__ARMEL__) || defined(__AARCH64EL__) || defined(_M_ARM64) -# define SQLITE_BYTEORDER 1234 -# elif defined(sparc) || defined(__ppc__) || \ - defined(__ARMEB__) || defined(__AARCH64EB__) -# define SQLITE_BYTEORDER 4321 +# define SQLITE_BYTEORDER 1234 +# elif defined(sparc) || defined(__ARMEB__) || defined(__AARCH64EB__) +# define SQLITE_BYTEORDER 4321 # else # define SQLITE_BYTEORDER 0 # endif @@ -205801,7 +209183,7 @@ static int nodeAcquire( ** increase its reference count and return it. */ if( (pNode = nodeHashLookup(pRtree, iNode))!=0 ){ - if( pParent && pParent!=pNode->pParent ){ + if( pParent && ALWAYS(pParent!=pNode->pParent) ){ RTREE_IS_CORRUPT(pRtree); return SQLITE_CORRUPT_VTAB; } @@ -205821,11 +209203,9 @@ static int nodeAcquire( } } if( pRtree->pNodeBlob==0 ){ - char *zTab = sqlite3_mprintf("%s_node", pRtree->zName); - if( zTab==0 ) return SQLITE_NOMEM; - rc = sqlite3_blob_open(pRtree->db, pRtree->zDb, zTab, "data", iNode, 0, + rc = sqlite3_blob_open(pRtree->db, pRtree->zDb, pRtree->zNodeName, + "data", iNode, 0, &pRtree->pNodeBlob); - sqlite3_free(zTab); } if( rc ){ nodeBlobReset(pRtree); @@ -207166,8 +210546,12 @@ static int rtreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ pIdxInfo->idxNum = 2; pIdxInfo->needToFreeIdxStr = 1; - if( iIdx>0 && 0==(pIdxInfo->idxStr = sqlite3_mprintf("%s", zIdxStr)) ){ - return SQLITE_NOMEM; + if( iIdx>0 ){ + pIdxInfo->idxStr = sqlite3_malloc( iIdx+1 ); + if( pIdxInfo->idxStr==0 ){ + return SQLITE_NOMEM; + } + memcpy(pIdxInfo->idxStr, zIdxStr, iIdx+1); } nRow = pRtree->nRowEst >> (iIdx/2); @@ -207246,31 +210630,22 @@ static void cellUnion(Rtree *pRtree, RtreeCell *p1, RtreeCell *p2){ */ static int cellContains(Rtree *pRtree, RtreeCell *p1, RtreeCell *p2){ int ii; - int isInt = (pRtree->eCoordType==RTREE_COORD_INT32); - for(ii=0; iinDim2; ii+=2){ - RtreeCoord *a1 = &p1->aCoord[ii]; - RtreeCoord *a2 = &p2->aCoord[ii]; - if( (!isInt && (a2[0].fa1[1].f)) - || ( isInt && (a2[0].ia1[1].i)) - ){ - return 0; + if( pRtree->eCoordType==RTREE_COORD_INT32 ){ + for(ii=0; iinDim2; ii+=2){ + RtreeCoord *a1 = &p1->aCoord[ii]; + RtreeCoord *a2 = &p2->aCoord[ii]; + if( a2[0].ia1[1].i ) return 0; + } + }else{ + for(ii=0; iinDim2; ii+=2){ + RtreeCoord *a1 = &p1->aCoord[ii]; + RtreeCoord *a2 = &p2->aCoord[ii]; + if( a2[0].fa1[1].f ) return 0; } } return 1; } -/* -** Return the amount cell p would grow by if it were unioned with pCell. -*/ -static RtreeDValue cellGrowth(Rtree *pRtree, RtreeCell *p, RtreeCell *pCell){ - RtreeDValue area; - RtreeCell cell; - memcpy(&cell, p, sizeof(RtreeCell)); - area = cellArea(pRtree, &cell); - cellUnion(pRtree, &cell, pCell); - return (cellArea(pRtree, &cell)-area); -} - static RtreeDValue cellOverlap( Rtree *pRtree, RtreeCell *p, @@ -207317,38 +210692,52 @@ static int ChooseLeaf( for(ii=0; rc==SQLITE_OK && ii<(pRtree->iDepth-iHeight); ii++){ int iCell; sqlite3_int64 iBest = 0; - + int bFound = 0; RtreeDValue fMinGrowth = RTREE_ZERO; RtreeDValue fMinArea = RTREE_ZERO; - int nCell = NCELL(pNode); - RtreeCell cell; RtreeNode *pChild = 0; - RtreeCell *aCell = 0; - - /* Select the child node which will be enlarged the least if pCell - ** is inserted into it. Resolve ties by choosing the entry with - ** the smallest area. + /* First check to see if there is are any cells in pNode that completely + ** contains pCell. If two or more cells in pNode completely contain pCell + ** then pick the smallest. */ for(iCell=0; iCell1 ){ - int iLeft = 0; - int iRight = 0; - - int nLeft = nIdx/2; - int nRight = nIdx-nLeft; - int *aLeft = aIdx; - int *aRight = &aIdx[nLeft]; - - SortByDistance(aLeft, nLeft, aDistance, aSpare); - SortByDistance(aRight, nRight, aDistance, aSpare); - - memcpy(aSpare, aLeft, sizeof(int)*nLeft); - aLeft = aSpare; - - while( iLeftnDim; iDim++){ - aCenterCoord[iDim] += DCOORD(aCell[ii].aCoord[iDim*2]); - aCenterCoord[iDim] += DCOORD(aCell[ii].aCoord[iDim*2+1]); - } - } - for(iDim=0; iDimnDim; iDim++){ - aCenterCoord[iDim] = (aCenterCoord[iDim]/(nCell*(RtreeDValue)2)); - } - - for(ii=0; iinDim; iDim++){ - RtreeDValue coord = (DCOORD(aCell[ii].aCoord[iDim*2+1]) - - DCOORD(aCell[ii].aCoord[iDim*2])); - aDistance[ii] += (coord-aCenterCoord[iDim])*(coord-aCenterCoord[iDim]); - } - } - - SortByDistance(aOrder, nCell, aDistance, aSpare); - nodeZero(pRtree, pNode); - - for(ii=0; rc==SQLITE_OK && ii<(nCell-(RTREE_MINCELLS(pRtree)+1)); ii++){ - RtreeCell *p = &aCell[aOrder[ii]]; - nodeInsertCell(pRtree, pNode, p); - if( p->iRowid==pCell->iRowid ){ - if( iHeight==0 ){ - rc = rowidWrite(pRtree, p->iRowid, pNode->iNode); - }else{ - rc = parentWrite(pRtree, p->iRowid, pNode->iNode); - } - } - } - if( rc==SQLITE_OK ){ - rc = fixBoundingBox(pRtree, pNode); - } - for(; rc==SQLITE_OK && iiiNode currently contains - ** the height of the sub-tree headed by the cell. - */ - RtreeNode *pInsert; - RtreeCell *p = &aCell[aOrder[ii]]; - rc = ChooseLeaf(pRtree, p, iHeight, &pInsert); - if( rc==SQLITE_OK ){ - int rc2; - rc = rtreeInsertCell(pRtree, pInsert, p, iHeight); - rc2 = nodeRelease(pRtree, pInsert); - if( rc==SQLITE_OK ){ - rc = rc2; - } - } - } - - sqlite3_free(aCell); - return rc; -} - /* ** Insert cell pCell into node pNode. Node pNode is the head of a ** subtree iHeight high (leaf nodes have iHeight==0). @@ -208097,12 +211314,7 @@ static int rtreeInsertCell( } } if( nodeInsertCell(pRtree, pNode, pCell) ){ - if( iHeight<=pRtree->iReinsertHeight || pNode->iNode==1){ - rc = SplitNode(pRtree, pNode, pCell, iHeight); - }else{ - pRtree->iReinsertHeight = iHeight; - rc = Reinsert(pRtree, pNode, pCell, iHeight); - } + rc = SplitNode(pRtree, pNode, pCell, iHeight); }else{ rc = AdjustTree(pRtree, pNode, pCell); if( ALWAYS(rc==SQLITE_OK) ){ @@ -208445,7 +211657,6 @@ static int rtreeUpdate( } if( rc==SQLITE_OK ){ int rc2; - pRtree->iReinsertHeight = -1; rc = rtreeInsertCell(pRtree, pLeaf, &cell, 0); rc2 = nodeRelease(pRtree, pLeaf); if( rc==SQLITE_OK ){ @@ -208586,8 +211797,11 @@ static int rtreeShadowName(const char *zName){ return 0; } +/* Forward declaration */ +static int rtreeIntegrity(sqlite3_vtab*, const char*, const char*, int, char**); + static sqlite3_module rtreeModule = { - 3, /* iVersion */ + 4, /* iVersion */ rtreeCreate, /* xCreate - create a table */ rtreeConnect, /* xConnect - connect to an existing table */ rtreeBestIndex, /* xBestIndex - Determine search strategy */ @@ -208610,7 +211824,8 @@ static sqlite3_module rtreeModule = { rtreeSavepoint, /* xSavepoint */ 0, /* xRelease */ 0, /* xRollbackTo */ - rtreeShadowName /* xShadowName */ + rtreeShadowName, /* xShadowName */ + rtreeIntegrity /* xIntegrity */ }; static int rtreeSqlInit( @@ -208703,7 +211918,7 @@ static int rtreeSqlInit( } sqlite3_free(zSql); } - if( pRtree->nAux ){ + if( pRtree->nAux && rc!=SQLITE_NOMEM ){ pRtree->zReadAuxSql = sqlite3_mprintf( "SELECT * FROM \"%w\".\"%w_rowid\" WHERE rowid=?1", zDb, zPrefix); @@ -208866,22 +212081,27 @@ static int rtreeInit( } sqlite3_vtab_config(db, SQLITE_VTAB_CONSTRAINT_SUPPORT, 1); + sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS); + /* Allocate the sqlite3_vtab structure */ nDb = (int)strlen(argv[1]); nName = (int)strlen(argv[2]); - pRtree = (Rtree *)sqlite3_malloc64(sizeof(Rtree)+nDb+nName+2); + pRtree = (Rtree *)sqlite3_malloc64(sizeof(Rtree)+nDb+nName*2+8); if( !pRtree ){ return SQLITE_NOMEM; } - memset(pRtree, 0, sizeof(Rtree)+nDb+nName+2); + memset(pRtree, 0, sizeof(Rtree)+nDb+nName*2+8); pRtree->nBusy = 1; pRtree->base.pModule = &rtreeModule; pRtree->zDb = (char *)&pRtree[1]; pRtree->zName = &pRtree->zDb[nDb+1]; + pRtree->zNodeName = &pRtree->zName[nName+1]; pRtree->eCoordType = (u8)eCoordType; memcpy(pRtree->zDb, argv[1], nDb); memcpy(pRtree->zName, argv[2], nName); + memcpy(pRtree->zNodeName, argv[2], nName); + memcpy(&pRtree->zNodeName[nName], "_node", 6); /* Create/Connect to the underlying relational database schema. If @@ -209378,7 +212598,6 @@ static int rtreeCheckTable( ){ RtreeCheck check; /* Common context for various routines */ sqlite3_stmt *pStmt = 0; /* Used to find column count of rtree table */ - int bEnd = 0; /* True if transaction should be closed */ int nAux = 0; /* Number of extra columns. */ /* Initialize the context object */ @@ -209387,24 +212606,14 @@ static int rtreeCheckTable( check.zDb = zDb; check.zTab = zTab; - /* If there is not already an open transaction, open one now. This is - ** to ensure that the queries run as part of this integrity-check operate - ** on a consistent snapshot. */ - if( sqlite3_get_autocommit(db) ){ - check.rc = sqlite3_exec(db, "BEGIN", 0, 0, 0); - bEnd = 1; - } - /* Find the number of auxiliary columns */ - if( check.rc==SQLITE_OK ){ - pStmt = rtreeCheckPrepare(&check, "SELECT * FROM %Q.'%q_rowid'", zDb, zTab); - if( pStmt ){ - nAux = sqlite3_column_count(pStmt) - 2; - sqlite3_finalize(pStmt); - }else - if( check.rc!=SQLITE_NOMEM ){ - check.rc = SQLITE_OK; - } + pStmt = rtreeCheckPrepare(&check, "SELECT * FROM %Q.'%q_rowid'", zDb, zTab); + if( pStmt ){ + nAux = sqlite3_column_count(pStmt) - 2; + sqlite3_finalize(pStmt); + }else + if( check.rc!=SQLITE_NOMEM ){ + check.rc = SQLITE_OK; } /* Find number of dimensions in the rtree table. */ @@ -209435,15 +212644,35 @@ static int rtreeCheckTable( sqlite3_finalize(check.aCheckMapping[0]); sqlite3_finalize(check.aCheckMapping[1]); - /* If one was opened, close the transaction */ - if( bEnd ){ - int rc = sqlite3_exec(db, "END", 0, 0, 0); - if( check.rc==SQLITE_OK ) check.rc = rc; - } *pzReport = check.zReport; return check.rc; } +/* +** Implementation of the xIntegrity method for Rtree. +*/ +static int rtreeIntegrity( + sqlite3_vtab *pVtab, /* The virtual table to check */ + const char *zSchema, /* Schema in which the virtual table lives */ + const char *zName, /* Name of the virtual table */ + int isQuick, /* True for a quick_check */ + char **pzErr /* Write results here */ +){ + Rtree *pRtree = (Rtree*)pVtab; + int rc; + assert( pzErr!=0 && *pzErr==0 ); + UNUSED_PARAMETER(zSchema); + UNUSED_PARAMETER(zName); + UNUSED_PARAMETER(isQuick); + rc = rtreeCheckTable(pRtree->db, pRtree->zDb, pRtree->zName, pzErr); + if( rc==SQLITE_OK && *pzErr ){ + *pzErr = sqlite3_mprintf("In RTree %s.%s:\n%z", + pRtree->zDb, pRtree->zName, *pzErr); + if( (*pzErr)==0 ) rc = SQLITE_NOMEM; + } + return rc; +} + /* ** Usage: ** @@ -210765,24 +213994,28 @@ static int geopolyInit( (void)pAux; sqlite3_vtab_config(db, SQLITE_VTAB_CONSTRAINT_SUPPORT, 1); + sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS); /* Allocate the sqlite3_vtab structure */ nDb = strlen(argv[1]); nName = strlen(argv[2]); - pRtree = (Rtree *)sqlite3_malloc64(sizeof(Rtree)+nDb+nName+2); + pRtree = (Rtree *)sqlite3_malloc64(sizeof(Rtree)+nDb+nName*2+8); if( !pRtree ){ return SQLITE_NOMEM; } - memset(pRtree, 0, sizeof(Rtree)+nDb+nName+2); + memset(pRtree, 0, sizeof(Rtree)+nDb+nName*2+8); pRtree->nBusy = 1; pRtree->base.pModule = &rtreeModule; pRtree->zDb = (char *)&pRtree[1]; pRtree->zName = &pRtree->zDb[nDb+1]; + pRtree->zNodeName = &pRtree->zName[nName+1]; pRtree->eCoordType = RTREE_COORD_REAL32; pRtree->nDim = 2; pRtree->nDim2 = 4; memcpy(pRtree->zDb, argv[1], nDb); memcpy(pRtree->zName, argv[2], nName); + memcpy(pRtree->zNodeName, argv[2], nName); + memcpy(&pRtree->zNodeName[nName], "_node", 6); /* Create/Connect to the underlying relational database schema. If @@ -211196,7 +214429,6 @@ static int geopolyUpdate( } if( rc==SQLITE_OK ){ int rc2; - pRtree->iReinsertHeight = -1; rc = rtreeInsertCell(pRtree, pLeaf, &cell, 0); rc2 = nodeRelease(pRtree, pLeaf); if( rc==SQLITE_OK ){ @@ -211293,7 +214525,8 @@ static sqlite3_module geopolyModule = { rtreeSavepoint, /* xSavepoint */ 0, /* xRelease */ 0, /* xRollbackTo */ - rtreeShadowName /* xShadowName */ + rtreeShadowName, /* xShadowName */ + rtreeIntegrity /* xIntegrity */ }; static int sqlite3_geopoly_init(sqlite3 *db){ @@ -219307,7 +222540,8 @@ SQLITE_PRIVATE int sqlite3DbstatRegister(sqlite3 *db){ 0, /* xSavepoint */ 0, /* xRelease */ 0, /* xRollbackTo */ - 0 /* xShadowName */ + 0, /* xShadowName */ + 0 /* xIntegrity */ }; return sqlite3_create_module(db, "dbstat", &dbstat_module, 0); } @@ -219744,7 +222978,8 @@ SQLITE_PRIVATE int sqlite3DbpageRegister(sqlite3 *db){ 0, /* xSavepoint */ 0, /* xRelease */ 0, /* xRollbackTo */ - 0 /* xShadowName */ + 0, /* xShadowName */ + 0 /* xIntegrity */ }; return sqlite3_create_module(db, "sqlite_dbpage", &dbpage_module, 0); } @@ -219875,6 +223110,18 @@ struct sqlite3_changeset_iter { ** The data associated with each hash-table entry is a structure containing ** a subset of the initial values that the modified row contained at the ** start of the session. Or no initial values if the row was inserted. +** +** pDfltStmt: +** This is only used by the sqlite3changegroup_xxx() APIs, not by +** regular sqlite3_session objects. It is a SELECT statement that +** selects the default value for each table column. For example, +** if the table is +** +** CREATE TABLE xx(a DEFAULT 1, b, c DEFAULT 'abc') +** +** then this variable is the compiled version of: +** +** SELECT 1, NULL, 'abc' */ struct SessionTable { SessionTable *pNext; @@ -219883,10 +223130,12 @@ struct SessionTable { int bStat1; /* True if this is sqlite_stat1 */ int bRowid; /* True if this table uses rowid for PK */ const char **azCol; /* Column names */ + const char **azDflt; /* Default value expressions */ u8 *abPK; /* Array of primary key flags */ int nEntry; /* Total number of entries in hash table */ int nChange; /* Size of apChange[] array */ SessionChange **apChange; /* Hash table buckets */ + sqlite3_stmt *pDfltStmt; }; /* @@ -220055,6 +223304,7 @@ struct SessionTable { struct SessionChange { u8 op; /* One of UPDATE, DELETE, INSERT */ u8 bIndirect; /* True if this change is "indirect" */ + u16 nRecordField; /* Number of fields in aRecord[] */ int nMaxSize; /* Max size of eventual changeset record */ int nRecord; /* Number of bytes in buffer aRecord[] */ u8 *aRecord; /* Buffer containing old.* record */ @@ -220080,7 +223330,7 @@ static int sessionVarintLen(int iVal){ ** Read a varint value from aBuf[] into *piVal. Return the number of ** bytes read. */ -static int sessionVarintGet(u8 *aBuf, int *piVal){ +static int sessionVarintGet(const u8 *aBuf, int *piVal){ return getVarint32(aBuf, *piVal); } @@ -220343,9 +223593,11 @@ static int sessionPreupdateHash( ** Return the number of bytes of space occupied by the value (including ** the type byte). */ -static int sessionSerialLen(u8 *a){ - int e = *a; +static int sessionSerialLen(const u8 *a){ + int e; int n; + assert( a!=0 ); + e = *a; if( e==0 || e==0xFF ) return 1; if( e==SQLITE_NULL ) return 1; if( e==SQLITE_INTEGER || e==SQLITE_FLOAT ) return 9; @@ -220750,13 +224002,14 @@ static int sessionGrowHash( ** ** For example, if the table is declared as: ** -** CREATE TABLE tbl1(w, x, y, z, PRIMARY KEY(w, z)); +** CREATE TABLE tbl1(w, x DEFAULT 'abc', y, z, PRIMARY KEY(w, z)); ** -** Then the four output variables are populated as follows: +** Then the five output variables are populated as follows: ** ** *pnCol = 4 ** *pzTab = "tbl1" ** *pazCol = {"w", "x", "y", "z"} +** *pazDflt = {NULL, 'abc', NULL, NULL} ** *pabPK = {1, 0, 0, 1} ** ** All returned buffers are part of the same single allocation, which must @@ -220770,6 +224023,7 @@ static int sessionTableInfo( int *pnCol, /* OUT: number of columns */ const char **pzTab, /* OUT: Copy of zThis */ const char ***pazCol, /* OUT: Array of column names for table */ + const char ***pazDflt, /* OUT: Array of default value expressions */ u8 **pabPK, /* OUT: Array of booleans - true for PK col */ int *pbRowid /* OUT: True if only PK is a rowid */ ){ @@ -220782,11 +224036,18 @@ static int sessionTableInfo( int i; u8 *pAlloc = 0; char **azCol = 0; + char **azDflt = 0; u8 *abPK = 0; int bRowid = 0; /* Set to true to use rowid as PK */ assert( pazCol && pabPK ); + *pazCol = 0; + *pabPK = 0; + *pnCol = 0; + if( pzTab ) *pzTab = 0; + if( pazDflt ) *pazDflt = 0; + nThis = sqlite3Strlen30(zThis); if( nThis==12 && 0==sqlite3_stricmp("sqlite_stat1", zThis) ){ rc = sqlite3_table_column_metadata(db, zDb, zThis, 0, 0, 0, 0, 0, 0); @@ -220800,39 +224061,28 @@ static int sessionTableInfo( }else if( rc==SQLITE_ERROR ){ zPragma = sqlite3_mprintf(""); }else{ - *pazCol = 0; - *pabPK = 0; - *pnCol = 0; - if( pzTab ) *pzTab = 0; return rc; } }else{ zPragma = sqlite3_mprintf("PRAGMA '%q'.table_info('%q')", zDb, zThis); } if( !zPragma ){ - *pazCol = 0; - *pabPK = 0; - *pnCol = 0; - if( pzTab ) *pzTab = 0; return SQLITE_NOMEM; } rc = sqlite3_prepare_v2(db, zPragma, -1, &pStmt, 0); sqlite3_free(zPragma); if( rc!=SQLITE_OK ){ - *pazCol = 0; - *pabPK = 0; - *pnCol = 0; - if( pzTab ) *pzTab = 0; return rc; } nByte = nThis + 1; bRowid = (pbRowid!=0); while( SQLITE_ROW==sqlite3_step(pStmt) ){ - nByte += sqlite3_column_bytes(pStmt, 1); + nByte += sqlite3_column_bytes(pStmt, 1); /* name */ + nByte += sqlite3_column_bytes(pStmt, 4); /* dflt_value */ nDbCol++; - if( sqlite3_column_int(pStmt, 5) ) bRowid = 0; + if( sqlite3_column_int(pStmt, 5) ) bRowid = 0; /* pk */ } if( nDbCol==0 ) bRowid = 0; nDbCol += bRowid; @@ -220840,15 +224090,18 @@ static int sessionTableInfo( rc = sqlite3_reset(pStmt); if( rc==SQLITE_OK ){ - nByte += nDbCol * (sizeof(const char *) + sizeof(u8) + 1); + nByte += nDbCol * (sizeof(const char *)*2 + sizeof(u8) + 1 + 1); pAlloc = sessionMalloc64(pSession, nByte); if( pAlloc==0 ){ rc = SQLITE_NOMEM; + }else{ + memset(pAlloc, 0, nByte); } } if( rc==SQLITE_OK ){ azCol = (char **)pAlloc; - pAlloc = (u8 *)&azCol[nDbCol]; + azDflt = (char**)&azCol[nDbCol]; + pAlloc = (u8 *)&azDflt[nDbCol]; abPK = (u8 *)pAlloc; pAlloc = &abPK[nDbCol]; if( pzTab ){ @@ -220868,11 +224121,21 @@ static int sessionTableInfo( } while( SQLITE_ROW==sqlite3_step(pStmt) ){ int nName = sqlite3_column_bytes(pStmt, 1); + int nDflt = sqlite3_column_bytes(pStmt, 4); const unsigned char *zName = sqlite3_column_text(pStmt, 1); + const unsigned char *zDflt = sqlite3_column_text(pStmt, 4); + if( zName==0 ) break; memcpy(pAlloc, zName, nName+1); azCol[i] = (char *)pAlloc; pAlloc += nName+1; + if( zDflt ){ + memcpy(pAlloc, zDflt, nDflt+1); + azDflt[i] = (char *)pAlloc; + pAlloc += nDflt+1; + }else{ + azDflt[i] = 0; + } abPK[i] = sqlite3_column_int(pStmt, 5); i++; } @@ -220883,14 +224146,11 @@ static int sessionTableInfo( ** free any allocation made. An error code will be returned in this case. */ if( rc==SQLITE_OK ){ - *pazCol = (const char **)azCol; + *pazCol = (const char**)azCol; + if( pazDflt ) *pazDflt = (const char**)azDflt; *pabPK = abPK; *pnCol = nDbCol; }else{ - *pazCol = 0; - *pabPK = 0; - *pnCol = 0; - if( pzTab ) *pzTab = 0; sessionFree(pSession, azCol); } if( pbRowid ) *pbRowid = bRowid; @@ -220899,10 +224159,9 @@ static int sessionTableInfo( } /* -** This function is only called from within a pre-update handler for a -** write to table pTab, part of session pSession. If this is the first -** write to this table, initalize the SessionTable.nCol, azCol[] and -** abPK[] arrays accordingly. +** This function is called to initialize the SessionTable.nCol, azCol[] +** abPK[] and azDflt[] members of SessionTable object pTab. If these +** fields are already initilialized, this function is a no-op. ** ** If an error occurs, an error code is stored in sqlite3_session.rc and ** non-zero returned. Or, if no error occurs but the table has no primary @@ -220910,15 +224169,22 @@ static int sessionTableInfo( ** indicate that updates on this table should be ignored. SessionTable.abPK ** is set to NULL in this case. */ -static int sessionInitTable(sqlite3_session *pSession, SessionTable *pTab){ +static int sessionInitTable( + sqlite3_session *pSession, /* Optional session handle */ + SessionTable *pTab, /* Table object to initialize */ + sqlite3 *db, /* Database handle to read schema from */ + const char *zDb /* Name of db - "main", "temp" etc. */ +){ + int rc = SQLITE_OK; + if( pTab->nCol==0 ){ u8 *abPK; assert( pTab->azCol==0 || pTab->abPK==0 ); - pSession->rc = sessionTableInfo(pSession, pSession->db, pSession->zDb, - pTab->zName, &pTab->nCol, 0, &pTab->azCol, &abPK, - (pSession->bImplicitPK ? &pTab->bRowid : 0) + rc = sessionTableInfo(pSession, db, zDb, + pTab->zName, &pTab->nCol, 0, &pTab->azCol, &pTab->azDflt, &abPK, + ((pSession==0 || pSession->bImplicitPK) ? &pTab->bRowid : 0) ); - if( pSession->rc==SQLITE_OK ){ + if( rc==SQLITE_OK ){ int i; for(i=0; inCol; i++){ if( abPK[i] ){ @@ -220930,14 +224196,321 @@ static int sessionInitTable(sqlite3_session *pSession, SessionTable *pTab){ pTab->bStat1 = 1; } - if( pSession->bEnableSize ){ + if( pSession && pSession->bEnableSize ){ pSession->nMaxChangesetSize += ( 1 + sessionVarintLen(pTab->nCol) + pTab->nCol + strlen(pTab->zName)+1 ); } } } - return (pSession->rc || pTab->abPK==0); + + if( pSession ){ + pSession->rc = rc; + return (rc || pTab->abPK==0); + } + return rc; +} + +/* +** Re-initialize table object pTab. +*/ +static int sessionReinitTable(sqlite3_session *pSession, SessionTable *pTab){ + int nCol = 0; + const char **azCol = 0; + const char **azDflt = 0; + u8 *abPK = 0; + int bRowid = 0; + + assert( pSession->rc==SQLITE_OK ); + + pSession->rc = sessionTableInfo(pSession, pSession->db, pSession->zDb, + pTab->zName, &nCol, 0, &azCol, &azDflt, &abPK, + (pSession->bImplicitPK ? &bRowid : 0) + ); + if( pSession->rc==SQLITE_OK ){ + if( pTab->nCol>nCol || pTab->bRowid!=bRowid ){ + pSession->rc = SQLITE_SCHEMA; + }else{ + int ii; + int nOldCol = pTab->nCol; + for(ii=0; iinCol ){ + if( pTab->abPK[ii]!=abPK[ii] ){ + pSession->rc = SQLITE_SCHEMA; + } + }else if( abPK[ii] ){ + pSession->rc = SQLITE_SCHEMA; + } + } + + if( pSession->rc==SQLITE_OK ){ + const char **a = pTab->azCol; + pTab->azCol = azCol; + pTab->nCol = nCol; + pTab->azDflt = azDflt; + pTab->abPK = abPK; + azCol = a; + } + if( pSession->bEnableSize ){ + pSession->nMaxChangesetSize += (nCol - nOldCol); + pSession->nMaxChangesetSize += sessionVarintLen(nCol); + pSession->nMaxChangesetSize -= sessionVarintLen(nOldCol); + } + } + } + + sqlite3_free((char*)azCol); + return pSession->rc; +} + +/* +** Session-change object (*pp) contains an old.* record with fewer than +** nCol fields. This function updates it with the default values for +** the missing fields. +*/ +static void sessionUpdateOneChange( + sqlite3_session *pSession, /* For memory accounting */ + int *pRc, /* IN/OUT: Error code */ + SessionChange **pp, /* IN/OUT: Change object to update */ + int nCol, /* Number of columns now in table */ + sqlite3_stmt *pDflt /* SELECT */ +){ + SessionChange *pOld = *pp; + + while( pOld->nRecordFieldnRecordField; + int eType = sqlite3_column_type(pDflt, iField); + switch( eType ){ + case SQLITE_NULL: + nIncr = 1; + break; + case SQLITE_INTEGER: + case SQLITE_FLOAT: + nIncr = 9; + break; + default: { + int n = sqlite3_column_bytes(pDflt, iField); + nIncr = 1 + sessionVarintLen(n) + n; + assert( eType==SQLITE_TEXT || eType==SQLITE_BLOB ); + break; + } + } + + nByte = nIncr + (sizeof(SessionChange) + pOld->nRecord); + pNew = sessionMalloc64(pSession, nByte); + if( pNew==0 ){ + *pRc = SQLITE_NOMEM; + return; + }else{ + memcpy(pNew, pOld, sizeof(SessionChange)); + pNew->aRecord = (u8*)&pNew[1]; + memcpy(pNew->aRecord, pOld->aRecord, pOld->nRecord); + pNew->aRecord[pNew->nRecord++] = (u8)eType; + switch( eType ){ + case SQLITE_INTEGER: { + i64 iVal = sqlite3_column_int64(pDflt, iField); + sessionPutI64(&pNew->aRecord[pNew->nRecord], iVal); + pNew->nRecord += 8; + break; + } + + case SQLITE_FLOAT: { + double rVal = sqlite3_column_double(pDflt, iField); + i64 iVal = 0; + memcpy(&iVal, &rVal, sizeof(rVal)); + sessionPutI64(&pNew->aRecord[pNew->nRecord], iVal); + pNew->nRecord += 8; + break; + } + + case SQLITE_TEXT: { + int n = sqlite3_column_bytes(pDflt, iField); + const char *z = (const char*)sqlite3_column_text(pDflt, iField); + pNew->nRecord += sessionVarintPut(&pNew->aRecord[pNew->nRecord], n); + memcpy(&pNew->aRecord[pNew->nRecord], z, n); + pNew->nRecord += n; + break; + } + + case SQLITE_BLOB: { + int n = sqlite3_column_bytes(pDflt, iField); + const u8 *z = (const u8*)sqlite3_column_blob(pDflt, iField); + pNew->nRecord += sessionVarintPut(&pNew->aRecord[pNew->nRecord], n); + memcpy(&pNew->aRecord[pNew->nRecord], z, n); + pNew->nRecord += n; + break; + } + + default: + assert( eType==SQLITE_NULL ); + break; + } + + sessionFree(pSession, pOld); + *pp = pOld = pNew; + pNew->nRecordField++; + pNew->nMaxSize += nIncr; + if( pSession ){ + pSession->nMaxChangesetSize += nIncr; + } + } + } +} + +/* +** Ensure that there is room in the buffer to append nByte bytes of data. +** If not, use sqlite3_realloc() to grow the buffer so that there is. +** +** If successful, return zero. Otherwise, if an OOM condition is encountered, +** set *pRc to SQLITE_NOMEM and return non-zero. +*/ +static int sessionBufferGrow(SessionBuffer *p, i64 nByte, int *pRc){ +#define SESSION_MAX_BUFFER_SZ (0x7FFFFF00 - 1) + i64 nReq = p->nBuf + nByte; + if( *pRc==SQLITE_OK && nReq>p->nAlloc ){ + u8 *aNew; + i64 nNew = p->nAlloc ? p->nAlloc : 128; + + do { + nNew = nNew*2; + }while( nNewSESSION_MAX_BUFFER_SZ ){ + nNew = SESSION_MAX_BUFFER_SZ; + if( nNewaBuf, nNew); + if( 0==aNew ){ + *pRc = SQLITE_NOMEM; + }else{ + p->aBuf = aNew; + p->nAlloc = nNew; + } + } + return (*pRc!=SQLITE_OK); +} + + +/* +** This function is a no-op if *pRc is other than SQLITE_OK when it is +** called. Otherwise, append a string to the buffer. All bytes in the string +** up to (but not including) the nul-terminator are written to the buffer. +** +** If an OOM condition is encountered, set *pRc to SQLITE_NOMEM before +** returning. +*/ +static void sessionAppendStr( + SessionBuffer *p, + const char *zStr, + int *pRc +){ + int nStr = sqlite3Strlen30(zStr); + if( 0==sessionBufferGrow(p, nStr+1, pRc) ){ + memcpy(&p->aBuf[p->nBuf], zStr, nStr); + p->nBuf += nStr; + p->aBuf[p->nBuf] = 0x00; + } +} + +/* +** Format a string using printf() style formatting and then append it to the +** buffer using sessionAppendString(). +*/ +static void sessionAppendPrintf( + SessionBuffer *p, /* Buffer to append to */ + int *pRc, + const char *zFmt, + ... +){ + if( *pRc==SQLITE_OK ){ + char *zApp = 0; + va_list ap; + va_start(ap, zFmt); + zApp = sqlite3_vmprintf(zFmt, ap); + if( zApp==0 ){ + *pRc = SQLITE_NOMEM; + }else{ + sessionAppendStr(p, zApp, pRc); + } + va_end(ap); + sqlite3_free(zApp); + } +} + +/* +** Prepare a statement against database handle db that SELECTs a single +** row containing the default values for each column in table pTab. For +** example, if pTab is declared as: +** +** CREATE TABLE pTab(a PRIMARY KEY, b DEFAULT 123, c DEFAULT 'abcd'); +** +** Then this function prepares and returns the SQL statement: +** +** SELECT NULL, 123, 'abcd'; +*/ +static int sessionPrepareDfltStmt( + sqlite3 *db, /* Database handle */ + SessionTable *pTab, /* Table to prepare statement for */ + sqlite3_stmt **ppStmt /* OUT: Statement handle */ +){ + SessionBuffer sql = {0,0,0}; + int rc = SQLITE_OK; + const char *zSep = " "; + int ii = 0; + + *ppStmt = 0; + sessionAppendPrintf(&sql, &rc, "SELECT"); + for(ii=0; iinCol; ii++){ + const char *zDflt = pTab->azDflt[ii] ? pTab->azDflt[ii] : "NULL"; + sessionAppendPrintf(&sql, &rc, "%s%s", zSep, zDflt); + zSep = ", "; + } + if( rc==SQLITE_OK ){ + rc = sqlite3_prepare_v2(db, (const char*)sql.aBuf, -1, ppStmt, 0); + } + sqlite3_free(sql.aBuf); + + return rc; +} + +/* +** Table pTab has one or more existing change-records with old.* records +** with fewer than pTab->nCol columns. This function updates all such +** change-records with the default values for the missing columns. +*/ +static int sessionUpdateChanges(sqlite3_session *pSession, SessionTable *pTab){ + sqlite3_stmt *pStmt = 0; + int rc = pSession->rc; + + rc = sessionPrepareDfltStmt(pSession->db, pTab, &pStmt); + if( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){ + int ii = 0; + SessionChange **pp = 0; + for(ii=0; iinChange; ii++){ + for(pp=&pTab->apChange[ii]; *pp; pp=&((*pp)->pNext)){ + if( (*pp)->nRecordField!=pTab->nCol ){ + sessionUpdateOneChange(pSession, &rc, pp, pTab->nCol, pStmt); + } + } + } + } + + pSession->rc = rc; + rc = sqlite3_finalize(pStmt); + if( pSession->rc==SQLITE_OK ) pSession->rc = rc; + return pSession->rc; } /* @@ -221100,16 +224673,22 @@ static void sessionPreupdateOneChange( int iHash; int bNull = 0; int rc = SQLITE_OK; + int nExpect = 0; SessionStat1Ctx stat1 = {{0,0,0,0,0},0}; if( pSession->rc ) return; /* Load table details if required */ - if( sessionInitTable(pSession, pTab) ) return; + if( sessionInitTable(pSession, pTab, pSession->db, pSession->zDb) ) return; /* Check the number of columns in this xPreUpdate call matches the ** number of columns in the table. */ - if( (pTab->nCol-pTab->bRowid)!=pSession->hook.xCount(pSession->hook.pCtx) ){ + nExpect = pSession->hook.xCount(pSession->hook.pCtx); + if( (pTab->nCol-pTab->bRowid)nCol-pTab->bRowid)!=nExpect ){ pSession->rc = SQLITE_SCHEMA; return; } @@ -221186,7 +224765,7 @@ static void sessionPreupdateOneChange( } /* Allocate the change object */ - pC = (SessionChange *)sessionMalloc64(pSession, nByte); + pC = (SessionChange*)sessionMalloc64(pSession, nByte); if( !pC ){ rc = SQLITE_NOMEM; goto error_out; @@ -221219,6 +224798,7 @@ static void sessionPreupdateOneChange( if( pSession->bIndirect || pSession->hook.xDepth(pSession->hook.pCtx) ){ pC->bIndirect = 1; } + pC->nRecordField = pTab->nCol; pC->nRecord = nByte; pC->op = op; pC->pNext = pTab->apChange[iHash]; @@ -221598,7 +225178,7 @@ SQLITE_API int sqlite3session_diff( /* Locate and if necessary initialize the target table object */ rc = sessionFindTable(pSession, zTbl, &pTo); if( pTo==0 ) goto diff_out; - if( sessionInitTable(pSession, pTo) ){ + if( sessionInitTable(pSession, pTo, pSession->db, pSession->zDb) ){ rc = pSession->rc; goto diff_out; } @@ -221611,7 +225191,7 @@ SQLITE_API int sqlite3session_diff( int bRowid = 0; u8 *abPK; const char **azCol = 0; - rc = sessionTableInfo(0, db, zFrom, zTbl, &nCol, 0, &azCol, &abPK, + rc = sessionTableInfo(0, db, zFrom, zTbl, &nCol, 0, &azCol, 0, &abPK, pSession->bImplicitPK ? &bRowid : 0 ); if( rc==SQLITE_OK ){ @@ -221726,6 +225306,7 @@ static void sessionDeleteTable(sqlite3_session *pSession, SessionTable *pList){ sessionFree(pSession, p); } } + sqlite3_finalize(pTab->pDfltStmt); sessionFree(pSession, (char*)pTab->azCol); /* cast works around VC++ bug */ sessionFree(pSession, pTab->apChange); sessionFree(pSession, pTab); @@ -221758,9 +225339,7 @@ SQLITE_API void sqlite3session_delete(sqlite3_session *pSession){ ** associated hash-tables. */ sessionDeleteTable(pSession, pSession->pTable); - /* Assert that all allocations have been freed and then free the - ** session object itself. */ - assert( pSession->nMalloc==0 ); + /* Free the session object. */ sqlite3_free(pSession); } @@ -221831,48 +225410,6 @@ SQLITE_API int sqlite3session_attach( return rc; } -/* -** Ensure that there is room in the buffer to append nByte bytes of data. -** If not, use sqlite3_realloc() to grow the buffer so that there is. -** -** If successful, return zero. Otherwise, if an OOM condition is encountered, -** set *pRc to SQLITE_NOMEM and return non-zero. -*/ -static int sessionBufferGrow(SessionBuffer *p, i64 nByte, int *pRc){ -#define SESSION_MAX_BUFFER_SZ (0x7FFFFF00 - 1) - i64 nReq = p->nBuf + nByte; - if( *pRc==SQLITE_OK && nReq>p->nAlloc ){ - u8 *aNew; - i64 nNew = p->nAlloc ? p->nAlloc : 128; - - do { - nNew = nNew*2; - }while( nNewSESSION_MAX_BUFFER_SZ ){ - nNew = SESSION_MAX_BUFFER_SZ; - if( nNewaBuf, nNew); - if( 0==aNew ){ - *pRc = SQLITE_NOMEM; - }else{ - p->aBuf = aNew; - p->nAlloc = nNew; - } - } - return (*pRc!=SQLITE_OK); -} - /* ** Append the value passed as the second argument to the buffer passed ** as the first. @@ -221941,27 +225478,6 @@ static void sessionAppendBlob( } } -/* -** This function is a no-op if *pRc is other than SQLITE_OK when it is -** called. Otherwise, append a string to the buffer. All bytes in the string -** up to (but not including) the nul-terminator are written to the buffer. -** -** If an OOM condition is encountered, set *pRc to SQLITE_NOMEM before -** returning. -*/ -static void sessionAppendStr( - SessionBuffer *p, - const char *zStr, - int *pRc -){ - int nStr = sqlite3Strlen30(zStr); - if( 0==sessionBufferGrow(p, nStr+1, pRc) ){ - memcpy(&p->aBuf[p->nBuf], zStr, nStr); - p->nBuf += nStr; - p->aBuf[p->nBuf] = 0x00; - } -} - /* ** This function is a no-op if *pRc is other than SQLITE_OK when it is ** called. Otherwise, append the string representation of integer iVal @@ -221980,27 +225496,6 @@ static void sessionAppendInteger( sessionAppendStr(p, aBuf, pRc); } -static void sessionAppendPrintf( - SessionBuffer *p, /* Buffer to append to */ - int *pRc, - const char *zFmt, - ... -){ - if( *pRc==SQLITE_OK ){ - char *zApp = 0; - va_list ap; - va_start(ap, zFmt); - zApp = sqlite3_vmprintf(zFmt, ap); - if( zApp==0 ){ - *pRc = SQLITE_NOMEM; - }else{ - sessionAppendStr(p, zApp, pRc); - } - va_end(ap); - sqlite3_free(zApp); - } -} - /* ** This function is a no-op if *pRc is other than SQLITE_OK when it is ** called. Otherwise, append the string zStr enclosed in quotes (") and @@ -222491,26 +225986,16 @@ static int sessionGenerateChangeset( for(pTab=pSession->pTable; rc==SQLITE_OK && pTab; pTab=pTab->pNext){ if( pTab->nEntry ){ const char *zName = pTab->zName; - int nCol = 0; /* Number of columns in table */ - u8 *abPK = 0; /* Primary key array */ - const char **azCol = 0; /* Table columns */ int i; /* Used to iterate through hash buckets */ sqlite3_stmt *pSel = 0; /* SELECT statement to query table pTab */ int nRewind = buf.nBuf; /* Initial size of write buffer */ int nNoop; /* Size of buffer after writing tbl header */ - int bRowid = 0; + int nOldCol = pTab->nCol; /* Check the table schema is still Ok. */ - rc = sessionTableInfo( - 0, db, pSession->zDb, zName, &nCol, 0, &azCol, &abPK, - (pSession->bImplicitPK ? &bRowid : 0) - ); - if( rc==SQLITE_OK && ( - pTab->nCol!=nCol - || pTab->bRowid!=bRowid - || memcmp(abPK, pTab->abPK, nCol) - )){ - rc = SQLITE_SCHEMA; + rc = sessionReinitTable(pSession, pTab); + if( rc==SQLITE_OK && pTab->nCol!=nOldCol ){ + rc = sessionUpdateChanges(pSession, pTab); } /* Write a table header */ @@ -222518,8 +226003,8 @@ static int sessionGenerateChangeset( /* Build and compile a statement to execute: */ if( rc==SQLITE_OK ){ - rc = sessionSelectStmt( - db, 0, pSession->zDb, zName, bRowid, nCol, azCol, abPK, &pSel + rc = sessionSelectStmt(db, 0, pSession->zDb, + zName, pTab->bRowid, pTab->nCol, pTab->azCol, pTab->abPK, &pSel ); } @@ -222528,22 +226013,22 @@ static int sessionGenerateChangeset( SessionChange *p; /* Used to iterate through changes */ for(p=pTab->apChange[i]; rc==SQLITE_OK && p; p=p->pNext){ - rc = sessionSelectBind(pSel, nCol, abPK, p); + rc = sessionSelectBind(pSel, pTab->nCol, pTab->abPK, p); if( rc!=SQLITE_OK ) continue; if( sqlite3_step(pSel)==SQLITE_ROW ){ if( p->op==SQLITE_INSERT ){ int iCol; sessionAppendByte(&buf, SQLITE_INSERT, &rc); sessionAppendByte(&buf, p->bIndirect, &rc); - for(iCol=0; iColnCol; iCol++){ sessionAppendCol(&buf, pSel, iCol, &rc); } }else{ - assert( abPK!=0 ); /* Because sessionSelectStmt() returned ok */ - rc = sessionAppendUpdate(&buf, bPatchset, pSel, p, abPK); + assert( pTab->abPK!=0 ); + rc = sessionAppendUpdate(&buf, bPatchset, pSel, p, pTab->abPK); } }else if( p->op!=SQLITE_INSERT ){ - rc = sessionAppendDelete(&buf, bPatchset, p, nCol, abPK); + rc = sessionAppendDelete(&buf, bPatchset, p, pTab->nCol,pTab->abPK); } if( rc==SQLITE_OK ){ rc = sqlite3_reset(pSel); @@ -222568,7 +226053,6 @@ static int sessionGenerateChangeset( if( buf.nBuf==nNoop ){ buf.nBuf = nRewind; } - sqlite3_free((char*)azCol); /* cast works around VC++ bug */ } } @@ -224697,7 +228181,7 @@ static int sessionChangesetApply( sqlite3changeset_pk(pIter, &abPK, 0); rc = sessionTableInfo(0, db, "main", zNew, - &sApply.nCol, &zTab, &sApply.azCol, &sApply.abPK, &sApply.bRowid + &sApply.nCol, &zTab, &sApply.azCol, 0, &sApply.abPK, &sApply.bRowid ); if( rc!=SQLITE_OK ) break; for(i=0; iflags & SQLITE_FkNoAction; + + if( flags & SQLITE_CHANGESETAPPLY_FKNOACTION ){ + db->flags |= ((u64)SQLITE_FkNoAction); + db->aDb[0].pSchema->schema_cookie -= 32; + } + if( rc==SQLITE_OK ){ rc = sessionChangesetApply( db, pIter, xFilter, xConflict, pCtx, ppRebase, pnRebase, flags ); } + + if( (flags & SQLITE_CHANGESETAPPLY_FKNOACTION) && savedFlag==0 ){ + assert( db->flags & SQLITE_FkNoAction ); + db->flags &= ~((u64)SQLITE_FkNoAction); + db->aDb[0].pSchema->schema_cookie -= 32; + } return rc; } @@ -224921,6 +228418,9 @@ struct sqlite3_changegroup { int rc; /* Error code */ int bPatch; /* True to accumulate patchsets */ SessionTable *pList; /* List of tables in current patch */ + + sqlite3 *db; /* Configured by changegroup_schema() */ + char *zDb; /* Configured by changegroup_schema() */ }; /* @@ -224941,6 +228441,7 @@ static int sessionChangeMerge( ){ SessionChange *pNew = 0; int rc = SQLITE_OK; + assert( aRec!=0 ); if( !pExist ){ pNew = (SessionChange *)sqlite3_malloc64(sizeof(SessionChange) + nRec); @@ -225106,6 +228607,114 @@ static int sessionChangeMerge( return rc; } +/* +** Check if a changeset entry with nCol columns and the PK array passed +** as the final argument to this function is compatible with SessionTable +** pTab. If so, return 1. Otherwise, if they are incompatible in some way, +** return 0. +*/ +static int sessionChangesetCheckCompat( + SessionTable *pTab, + int nCol, + u8 *abPK +){ + if( pTab->azCol && nColnCol ){ + int ii; + for(ii=0; iinCol; ii++){ + u8 bPK = (ii < nCol) ? abPK[ii] : 0; + if( pTab->abPK[ii]!=bPK ) return 0; + } + return 1; + } + return (pTab->nCol==nCol && 0==memcmp(abPK, pTab->abPK, nCol)); +} + +static int sessionChangesetExtendRecord( + sqlite3_changegroup *pGrp, + SessionTable *pTab, + int nCol, + int op, + const u8 *aRec, + int nRec, + SessionBuffer *pOut +){ + int rc = SQLITE_OK; + int ii = 0; + + assert( pTab->azCol ); + assert( nColnCol ); + + pOut->nBuf = 0; + if( op==SQLITE_INSERT || (op==SQLITE_DELETE && pGrp->bPatch==0) ){ + /* Append the missing default column values to the record. */ + sessionAppendBlob(pOut, aRec, nRec, &rc); + if( rc==SQLITE_OK && pTab->pDfltStmt==0 ){ + rc = sessionPrepareDfltStmt(pGrp->db, pTab, &pTab->pDfltStmt); + } + for(ii=nCol; rc==SQLITE_OK && iinCol; ii++){ + int eType = sqlite3_column_type(pTab->pDfltStmt, ii); + sessionAppendByte(pOut, eType, &rc); + switch( eType ){ + case SQLITE_FLOAT: + case SQLITE_INTEGER: { + i64 iVal; + if( eType==SQLITE_INTEGER ){ + iVal = sqlite3_column_int64(pTab->pDfltStmt, ii); + }else{ + double rVal = sqlite3_column_int64(pTab->pDfltStmt, ii); + memcpy(&iVal, &rVal, sizeof(i64)); + } + if( SQLITE_OK==sessionBufferGrow(pOut, 8, &rc) ){ + sessionPutI64(&pOut->aBuf[pOut->nBuf], iVal); + } + break; + } + + case SQLITE_BLOB: + case SQLITE_TEXT: { + int n = sqlite3_column_bytes(pTab->pDfltStmt, ii); + sessionAppendVarint(pOut, n, &rc); + if( eType==SQLITE_TEXT ){ + const u8 *z = (const u8*)sqlite3_column_text(pTab->pDfltStmt, ii); + sessionAppendBlob(pOut, z, n, &rc); + }else{ + const u8 *z = (const u8*)sqlite3_column_blob(pTab->pDfltStmt, ii); + sessionAppendBlob(pOut, z, n, &rc); + } + break; + } + + default: + assert( eType==SQLITE_NULL ); + break; + } + } + }else if( op==SQLITE_UPDATE ){ + /* Append missing "undefined" entries to the old.* record. And, if this + ** is an UPDATE, to the new.* record as well. */ + int iOff = 0; + if( pGrp->bPatch==0 ){ + for(ii=0; iinCol-nCol); ii++){ + sessionAppendByte(pOut, 0x00, &rc); + } + } + + sessionAppendBlob(pOut, &aRec[iOff], nRec-iOff, &rc); + for(ii=0; ii<(pTab->nCol-nCol); ii++){ + sessionAppendByte(pOut, 0x00, &rc); + } + }else{ + assert( op==SQLITE_DELETE && pGrp->bPatch ); + sessionAppendBlob(pOut, aRec, nRec, &rc); + } + + return rc; +} + /* ** Add all changes in the changeset traversed by the iterator passed as ** the first argument to the changegroup hash tables. @@ -225119,6 +228728,7 @@ static int sessionChangesetToHash( int nRec; int rc = SQLITE_OK; SessionTable *pTab = 0; + SessionBuffer rec = {0, 0, 0}; while( SQLITE_ROW==sessionChangesetNext(pIter, &aRec, &nRec, 0) ){ const char *zNew; @@ -225130,6 +228740,9 @@ static int sessionChangesetToHash( SessionChange *pExist = 0; SessionChange **pp; + /* Ensure that only changesets, or only patchsets, but not a mixture + ** of both, are being combined. It is an error to try to combine a + ** changeset and a patchset. */ if( pGrp->pList==0 ){ pGrp->bPatch = pIter->bPatchset; }else if( pIter->bPatchset!=pGrp->bPatch ){ @@ -225162,18 +228775,38 @@ static int sessionChangesetToHash( pTab->zName = (char*)&pTab->abPK[nCol]; memcpy(pTab->zName, zNew, nNew+1); + if( pGrp->db ){ + pTab->nCol = 0; + rc = sessionInitTable(0, pTab, pGrp->db, pGrp->zDb); + if( rc ){ + assert( pTab->azCol==0 ); + sqlite3_free(pTab); + break; + } + } + /* The new object must be linked on to the end of the list, not ** simply added to the start of it. This is to ensure that the ** tables within the output of sqlite3changegroup_output() are in ** the right order. */ for(ppTab=&pGrp->pList; *ppTab; ppTab=&(*ppTab)->pNext); *ppTab = pTab; - }else if( pTab->nCol!=nCol || memcmp(pTab->abPK, abPK, nCol) ){ + } + + if( !sessionChangesetCheckCompat(pTab, nCol, abPK) ){ rc = SQLITE_SCHEMA; break; } } + if( nColnCol ){ + assert( pGrp->db ); + rc = sessionChangesetExtendRecord(pGrp, pTab, nCol, op, aRec, nRec, &rec); + if( rc ) break; + aRec = rec.aBuf; + nRec = rec.nBuf; + } + if( sessionGrowHash(0, pIter->bPatchset, pTab) ){ rc = SQLITE_NOMEM; break; @@ -225211,6 +228844,7 @@ static int sessionChangesetToHash( } } + sqlite3_free(rec.aBuf); if( rc==SQLITE_OK ) rc = pIter->rc; return rc; } @@ -225297,6 +228931,31 @@ SQLITE_API int sqlite3changegroup_new(sqlite3_changegroup **pp){ return rc; } +/* +** Provide a database schema to the changegroup object. +*/ +SQLITE_API int sqlite3changegroup_schema( + sqlite3_changegroup *pGrp, + sqlite3 *db, + const char *zDb +){ + int rc = SQLITE_OK; + + if( pGrp->pList || pGrp->db ){ + /* Cannot add a schema after one or more calls to sqlite3changegroup_add(), + ** or after sqlite3changegroup_schema() has already been called. */ + rc = SQLITE_MISUSE; + }else{ + pGrp->zDb = sqlite3_mprintf("%s", zDb); + if( pGrp->zDb==0 ){ + rc = SQLITE_NOMEM; + }else{ + pGrp->db = db; + } + } + return rc; +} + /* ** Add the changeset currently stored in buffer pData, size nData bytes, ** to changeset-group p. @@ -225360,6 +229019,7 @@ SQLITE_API int sqlite3changegroup_output_strm( */ SQLITE_API void sqlite3changegroup_delete(sqlite3_changegroup *pGrp){ if( pGrp ){ + sqlite3_free(pGrp->zDb); sessionDeleteTable(0, pGrp->pList); sqlite3_free(pGrp); } @@ -225892,8 +229552,11 @@ struct Fts5PhraseIter { ** created with the "columnsize=0" option. ** ** xColumnText: -** This function attempts to retrieve the text of column iCol of the -** current document. If successful, (*pz) is set to point to a buffer +** If parameter iCol is less than zero, or greater than or equal to the +** number of columns in the table, SQLITE_RANGE is returned. +** +** Otherwise, this function attempts to retrieve the text of column iCol of +** the current document. If successful, (*pz) is set to point to a buffer ** containing the text in utf-8 encoding, (*pn) is set to the size in bytes ** (not characters) of the buffer and SQLITE_OK is returned. Otherwise, ** if an error occurs, an SQLite error code is returned and the final values @@ -225903,8 +229566,10 @@ struct Fts5PhraseIter { ** Returns the number of phrases in the current query expression. ** ** xPhraseSize: -** Returns the number of tokens in phrase iPhrase of the query. Phrases -** are numbered starting from zero. +** If parameter iCol is less than zero, or greater than or equal to the +** number of phrases in the current query, as returned by xPhraseCount, +** 0 is returned. Otherwise, this function returns the number of tokens in +** phrase iPhrase of the query. Phrases are numbered starting from zero. ** ** xInstCount: ** Set *pnInst to the total number of occurrences of all phrases within @@ -225920,12 +229585,13 @@ struct Fts5PhraseIter { ** Query for the details of phrase match iIdx within the current row. ** Phrase matches are numbered starting from zero, so the iIdx argument ** should be greater than or equal to zero and smaller than the value -** output by xInstCount(). +** output by xInstCount(). If iIdx is less than zero or greater than +** or equal to the value returned by xInstCount(), SQLITE_RANGE is returned. ** -** Usually, output parameter *piPhrase is set to the phrase number, *piCol +** Otherwise, output parameter *piPhrase is set to the phrase number, *piCol ** to the column in which it occurs and *piOff the token offset of the -** first token of the phrase. Returns SQLITE_OK if successful, or an error -** code (i.e. SQLITE_NOMEM) if an error occurs. +** first token of the phrase. SQLITE_OK is returned if successful, or an +** error code (i.e. SQLITE_NOMEM) if an error occurs. ** ** This API can be quite slow if used with an FTS5 table created with the ** "detail=none" or "detail=column" option. @@ -225951,6 +229617,10 @@ struct Fts5PhraseIter { ** Invoking Api.xUserData() returns a copy of the pointer passed as ** the third argument to pUserData. ** +** If parameter iPhrase is less than zero, or greater than or equal to +** the number of phrases in the query, as returned by xPhraseCount(), +** this function returns SQLITE_RANGE. +** ** If the callback function returns any value other than SQLITE_OK, the ** query is abandoned and the xQueryPhrase function returns immediately. ** If the returned value is SQLITE_DONE, xQueryPhrase returns SQLITE_OK. @@ -226065,9 +229735,42 @@ struct Fts5PhraseIter { ** ** xPhraseNextColumn() ** See xPhraseFirstColumn above. +** +** xQueryToken(pFts5, iPhrase, iToken, ppToken, pnToken) +** This is used to access token iToken of phrase iPhrase of the current +** query. Before returning, output parameter *ppToken is set to point +** to a buffer containing the requested token, and *pnToken to the +** size of this buffer in bytes. +** +** If iPhrase or iToken are less than zero, or if iPhrase is greater than +** or equal to the number of phrases in the query as reported by +** xPhraseCount(), or if iToken is equal to or greater than the number of +** tokens in the phrase, SQLITE_RANGE is returned and *ppToken and *pnToken + are both zeroed. +** +** The output text is not a copy of the query text that specified the +** token. It is the output of the tokenizer module. For tokendata=1 +** tables, this includes any embedded 0x00 and trailing data. +** +** xInstToken(pFts5, iIdx, iToken, ppToken, pnToken) +** This is used to access token iToken of phrase hit iIdx within the +** current row. If iIdx is less than zero or greater than or equal to the +** value returned by xInstCount(), SQLITE_RANGE is returned. Otherwise, +** output variable (*ppToken) is set to point to a buffer containing the +** matching document token, and (*pnToken) to the size of that buffer in +** bytes. This API is not available if the specified token matches a +** prefix query term. In that case both output variables are always set +** to 0. +** +** The output text is not a copy of the document text that was tokenized. +** It is the output of the tokenizer module. For tokendata=1 tables, this +** includes any embedded 0x00 and trailing data. +** +** This API can be quite slow if used with an FTS5 table created with the +** "detail=none" or "detail=column" option. */ struct Fts5ExtensionApi { - int iVersion; /* Currently always set to 2 */ + int iVersion; /* Currently always set to 3 */ void *(*xUserData)(Fts5Context*); @@ -226102,6 +229805,13 @@ struct Fts5ExtensionApi { int (*xPhraseFirstColumn)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*); void (*xPhraseNextColumn)(Fts5Context*, Fts5PhraseIter*, int *piCol); + + /* Below this point are iVersion>=3 only */ + int (*xQueryToken)(Fts5Context*, + int iPhrase, int iToken, + const char **ppToken, int *pnToken + ); + int (*xInstToken)(Fts5Context*, int iIdx, int iToken, const char**, int*); }; /* @@ -226576,6 +230286,7 @@ struct Fts5Config { char *zContent; /* content table */ char *zContentRowid; /* "content_rowid=" option value */ int bColumnsize; /* "columnsize=" option value (dflt==1) */ + int bTokendata; /* "tokendata=" option value (dflt==0) */ int eDetail; /* FTS5_DETAIL_XXX value */ char *zContentExprlist; Fts5Tokenizer *pTok; @@ -226764,17 +230475,19 @@ struct Fts5IndexIter { /* ** Values used as part of the flags argument passed to IndexQuery(). */ -#define FTS5INDEX_QUERY_PREFIX 0x0001 /* Prefix query */ -#define FTS5INDEX_QUERY_DESC 0x0002 /* Docs in descending rowid order */ -#define FTS5INDEX_QUERY_TEST_NOIDX 0x0004 /* Do not use prefix index */ -#define FTS5INDEX_QUERY_SCAN 0x0008 /* Scan query (fts5vocab) */ +#define FTS5INDEX_QUERY_PREFIX 0x0001 /* Prefix query */ +#define FTS5INDEX_QUERY_DESC 0x0002 /* Docs in descending rowid order */ +#define FTS5INDEX_QUERY_TEST_NOIDX 0x0004 /* Do not use prefix index */ +#define FTS5INDEX_QUERY_SCAN 0x0008 /* Scan query (fts5vocab) */ /* The following are used internally by the fts5_index.c module. They are ** defined here only to make it easier to avoid clashes with the flags ** above. */ -#define FTS5INDEX_QUERY_SKIPEMPTY 0x0010 -#define FTS5INDEX_QUERY_NOOUTPUT 0x0020 -#define FTS5INDEX_QUERY_SKIPHASH 0x0040 +#define FTS5INDEX_QUERY_SKIPEMPTY 0x0010 +#define FTS5INDEX_QUERY_NOOUTPUT 0x0020 +#define FTS5INDEX_QUERY_SKIPHASH 0x0040 +#define FTS5INDEX_QUERY_NOTOKENDATA 0x0080 +#define FTS5INDEX_QUERY_SCANONETERM 0x0100 /* ** Create/destroy an Fts5Index object. @@ -226843,6 +230556,10 @@ static void *sqlite3Fts5StructureRef(Fts5Index*); static void sqlite3Fts5StructureRelease(void*); static int sqlite3Fts5StructureTest(Fts5Index*, void*); +/* +** Used by xInstToken(): +*/ +static int sqlite3Fts5IterToken(Fts5IndexIter*, i64, int, int, const char**, int*); /* ** Insert or remove data to or from the index. Each time a document is @@ -226920,6 +230637,13 @@ static int sqlite3Fts5IndexLoadConfig(Fts5Index *p); static int sqlite3Fts5IndexGetOrigin(Fts5Index *p, i64 *piOrigin); static int sqlite3Fts5IndexContentlessDelete(Fts5Index *p, i64 iOrigin, i64 iRowid); +static void sqlite3Fts5IndexIterClearTokendata(Fts5IndexIter*); + +/* Used to populate hash tables for xInstToken in detail=none/column mode. */ +static int sqlite3Fts5IndexIterWriteTokendata( + Fts5IndexIter*, const char*, int, i64 iRowid, int iCol, int iOff +); + /* ** End of interface to code in fts5_index.c. **************************************************************************/ @@ -227025,6 +230749,7 @@ static void sqlite3Fts5HashScanNext(Fts5Hash*); static int sqlite3Fts5HashScanEof(Fts5Hash*); static void sqlite3Fts5HashScanEntry(Fts5Hash *, const char **pzTerm, /* OUT: term (nul-terminated) */ + int *pnTerm, /* OUT: Size of term in bytes */ const u8 **ppDoclist, /* OUT: pointer to doclist */ int *pnDoclist /* OUT: size of doclist in bytes */ ); @@ -227151,6 +230876,10 @@ static int sqlite3Fts5ExprClonePhrase(Fts5Expr*, int, Fts5Expr**); static int sqlite3Fts5ExprPhraseCollist(Fts5Expr *, int, const u8 **, int *); +static int sqlite3Fts5ExprQueryToken(Fts5Expr*, int, int, const char**, int*); +static int sqlite3Fts5ExprInstToken(Fts5Expr*, i64, int, int, int, int, const char**, int*); +static void sqlite3Fts5ExprClearTokens(Fts5Expr*); + /******************************************* ** The fts5_expr.c API above this point is used by the other hand-written ** C code in this module. The interfaces below this point are called by @@ -228865,15 +232594,19 @@ static int fts5CInstIterInit( */ typedef struct HighlightContext HighlightContext; struct HighlightContext { - CInstIter iter; /* Coalesced Instance Iterator */ - int iPos; /* Current token offset in zIn[] */ + /* Constant parameters to fts5HighlightCb() */ int iRangeStart; /* First token to include */ int iRangeEnd; /* If non-zero, last token to include */ const char *zOpen; /* Opening highlight */ const char *zClose; /* Closing highlight */ const char *zIn; /* Input text */ int nIn; /* Size of input text in bytes */ - int iOff; /* Current offset within zIn[] */ + + /* Variables modified by fts5HighlightCb() */ + CInstIter iter; /* Coalesced Instance Iterator */ + int iPos; /* Current token offset in zIn[] */ + int iOff; /* Have copied up to this offset in zIn[] */ + int bOpen; /* True if highlight is open */ char *zOut; /* Output value */ }; @@ -228906,8 +232639,8 @@ static int fts5HighlightCb( int tflags, /* Mask of FTS5_TOKEN_* flags */ const char *pToken, /* Buffer containing token */ int nToken, /* Size of token in bytes */ - int iStartOff, /* Start offset of token */ - int iEndOff /* End offset of token */ + int iStartOff, /* Start byte offset of token */ + int iEndOff /* End byte offset of token */ ){ HighlightContext *p = (HighlightContext*)pContext; int rc = SQLITE_OK; @@ -228923,30 +232656,55 @@ static int fts5HighlightCb( if( p->iRangeStart && iPos==p->iRangeStart ) p->iOff = iStartOff; } - if( iPos==p->iter.iStart ){ + /* If the parenthesis is open, and this token is not part of the current + ** phrase, and the starting byte offset of this token is past the point + ** that has currently been copied into the output buffer, close the + ** parenthesis. */ + if( p->bOpen + && (iPos<=p->iter.iStart || p->iter.iStart<0) + && iStartOff>p->iOff + ){ + fts5HighlightAppend(&rc, p, p->zClose, -1); + p->bOpen = 0; + } + + /* If this is the start of a new phrase, and the highlight is not open: + ** + ** * copy text from the input up to the start of the phrase, and + ** * open the highlight. + */ + if( iPos==p->iter.iStart && p->bOpen==0 ){ fts5HighlightAppend(&rc, p, &p->zIn[p->iOff], iStartOff - p->iOff); fts5HighlightAppend(&rc, p, p->zOpen, -1); p->iOff = iStartOff; + p->bOpen = 1; } if( iPos==p->iter.iEnd ){ - if( p->iRangeEnd>=0 && p->iter.iStartiRangeStart ){ + if( p->bOpen==0 ){ + assert( p->iRangeEnd>=0 ); fts5HighlightAppend(&rc, p, p->zOpen, -1); + p->bOpen = 1; } fts5HighlightAppend(&rc, p, &p->zIn[p->iOff], iEndOff - p->iOff); - fts5HighlightAppend(&rc, p, p->zClose, -1); p->iOff = iEndOff; + if( rc==SQLITE_OK ){ rc = fts5CInstIterNext(&p->iter); } } - if( p->iRangeEnd>=0 && iPos==p->iRangeEnd ){ + if( iPos==p->iRangeEnd ){ + if( p->bOpen ){ + if( p->iter.iStart>=0 && iPos>=p->iter.iStart ){ + fts5HighlightAppend(&rc, p, &p->zIn[p->iOff], iEndOff - p->iOff); + p->iOff = iEndOff; + } + fts5HighlightAppend(&rc, p, p->zClose, -1); + p->bOpen = 0; + } fts5HighlightAppend(&rc, p, &p->zIn[p->iOff], iEndOff - p->iOff); p->iOff = iEndOff; - if( iPos>=p->iter.iStart && iPositer.iEnd ){ - fts5HighlightAppend(&rc, p, p->zClose, -1); - } } return rc; @@ -228978,8 +232736,10 @@ static void fts5HighlightFunction( ctx.zClose = (const char*)sqlite3_value_text(apVal[2]); ctx.iRangeEnd = -1; rc = pApi->xColumnText(pFts, iCol, &ctx.zIn, &ctx.nIn); - - if( ctx.zIn ){ + if( rc==SQLITE_RANGE ){ + sqlite3_result_text(pCtx, "", -1, SQLITE_STATIC); + rc = SQLITE_OK; + }else if( ctx.zIn ){ if( rc==SQLITE_OK ){ rc = fts5CInstIterInit(pApi, pFts, iCol, &ctx.iter); } @@ -228987,6 +232747,9 @@ static void fts5HighlightFunction( if( rc==SQLITE_OK ){ rc = pApi->xTokenize(pFts, ctx.zIn, ctx.nIn, (void*)&ctx,fts5HighlightCb); } + if( ctx.bOpen ){ + fts5HighlightAppend(&rc, &ctx, ctx.zClose, -1); + } fts5HighlightAppend(&rc, &ctx, &ctx.zIn[ctx.iOff], ctx.nIn - ctx.iOff); if( rc==SQLITE_OK ){ @@ -229265,6 +233028,9 @@ static void fts5SnippetFunction( if( rc==SQLITE_OK ){ rc = pApi->xTokenize(pFts, ctx.zIn, ctx.nIn, (void*)&ctx,fts5HighlightCb); } + if( ctx.bOpen ){ + fts5HighlightAppend(&rc, &ctx, ctx.zClose, -1); + } if( ctx.iRangeEnd>=(nColSize-1) ){ fts5HighlightAppend(&rc, &ctx, &ctx.zIn[ctx.iOff], ctx.nIn - ctx.iOff); }else{ @@ -229540,6 +233306,7 @@ static void sqlite3Fts5BufferAppendBlob( ){ if( nData ){ if( fts5BufferGrow(pRc, pBuf, nData) ) return; + assert( pBuf->p!=0 ); memcpy(&pBuf->p[pBuf->n], pData, nData); pBuf->n += nData; } @@ -229641,6 +233408,7 @@ static int sqlite3Fts5PoslistNext64( i64 *piOff /* IN/OUT: Current offset */ ){ int i = *pi; + assert( a!=0 || i==0 ); if( i>=n ){ /* EOF */ *piOff = -1; @@ -229648,6 +233416,7 @@ static int sqlite3Fts5PoslistNext64( }else{ i64 iOff = *piOff; u32 iVal; + assert( a!=0 ); fts5FastGetVarint32(a, i, iVal); if( iVal<=1 ){ if( iVal==0 ){ @@ -230279,6 +234048,16 @@ static int fts5ConfigParseSpecial( return rc; } + if( sqlite3_strnicmp("tokendata", zCmd, nCmd)==0 ){ + if( (zArg[0]!='0' && zArg[0]!='1') || zArg[1]!='\0' ){ + *pzErr = sqlite3_mprintf("malformed tokendata=... directive"); + rc = SQLITE_ERROR; + }else{ + pConfig->bTokendata = (zArg[0]=='1'); + } + return rc; + } + *pzErr = sqlite3_mprintf("unrecognized option: \"%.*s\"", nCmd, zCmd); return SQLITE_ERROR; } @@ -231012,7 +234791,9 @@ struct Fts5ExprNode { struct Fts5ExprTerm { u8 bPrefix; /* True for a prefix term */ u8 bFirst; /* True if token must be first in column */ - char *zTerm; /* nul-terminated term */ + char *pTerm; /* Term data */ + int nQueryTerm; /* Effective size of term in bytes */ + int nFullTerm; /* Size of term in bytes incl. tokendata */ Fts5IndexIter *pIter; /* Iterator for this term */ Fts5ExprTerm *pSynonym; /* Pointer to first in list of synonyms */ }; @@ -231879,7 +235660,7 @@ static int fts5ExprNearInitAll( p->pIter = 0; } rc = sqlite3Fts5IndexQuery( - pExpr->pIndex, p->zTerm, (int)strlen(p->zTerm), + pExpr->pIndex, p->pTerm, p->nQueryTerm, (pTerm->bPrefix ? FTS5INDEX_QUERY_PREFIX : 0) | (pExpr->bDesc ? FTS5INDEX_QUERY_DESC : 0), pNear->pColset, @@ -232516,7 +236297,7 @@ static void fts5ExprPhraseFree(Fts5ExprPhrase *pPhrase){ Fts5ExprTerm *pSyn; Fts5ExprTerm *pNext; Fts5ExprTerm *pTerm = &pPhrase->aTerm[i]; - sqlite3_free(pTerm->zTerm); + sqlite3_free(pTerm->pTerm); sqlite3Fts5IterClose(pTerm->pIter); for(pSyn=pTerm->pSynonym; pSyn; pSyn=pNext){ pNext = pSyn->pSynonym; @@ -232614,6 +236395,7 @@ static Fts5ExprNearset *sqlite3Fts5ParseNearset( typedef struct TokenCtx TokenCtx; struct TokenCtx { Fts5ExprPhrase *pPhrase; + Fts5Config *pConfig; int rc; }; @@ -232647,8 +236429,12 @@ static int fts5ParseTokenize( rc = SQLITE_NOMEM; }else{ memset(pSyn, 0, (size_t)nByte); - pSyn->zTerm = ((char*)pSyn) + sizeof(Fts5ExprTerm) + sizeof(Fts5Buffer); - memcpy(pSyn->zTerm, pToken, nToken); + pSyn->pTerm = ((char*)pSyn) + sizeof(Fts5ExprTerm) + sizeof(Fts5Buffer); + pSyn->nFullTerm = pSyn->nQueryTerm = nToken; + if( pCtx->pConfig->bTokendata ){ + pSyn->nQueryTerm = (int)strlen(pSyn->pTerm); + } + memcpy(pSyn->pTerm, pToken, nToken); pSyn->pSynonym = pPhrase->aTerm[pPhrase->nTerm-1].pSynonym; pPhrase->aTerm[pPhrase->nTerm-1].pSynonym = pSyn; } @@ -232673,7 +236459,11 @@ static int fts5ParseTokenize( if( rc==SQLITE_OK ){ pTerm = &pPhrase->aTerm[pPhrase->nTerm++]; memset(pTerm, 0, sizeof(Fts5ExprTerm)); - pTerm->zTerm = sqlite3Fts5Strndup(&rc, pToken, nToken); + pTerm->pTerm = sqlite3Fts5Strndup(&rc, pToken, nToken); + pTerm->nFullTerm = pTerm->nQueryTerm = nToken; + if( pCtx->pConfig->bTokendata && rc==SQLITE_OK ){ + pTerm->nQueryTerm = (int)strlen(pTerm->pTerm); + } } } @@ -232740,6 +236530,7 @@ static Fts5ExprPhrase *sqlite3Fts5ParseTerm( memset(&sCtx, 0, sizeof(TokenCtx)); sCtx.pPhrase = pAppend; + sCtx.pConfig = pConfig; rc = fts5ParseStringFromToken(pToken, &z); if( rc==SQLITE_OK ){ @@ -232787,12 +236578,15 @@ static int sqlite3Fts5ExprClonePhrase( Fts5Expr **ppNew ){ int rc = SQLITE_OK; /* Return code */ - Fts5ExprPhrase *pOrig; /* The phrase extracted from pExpr */ + Fts5ExprPhrase *pOrig = 0; /* The phrase extracted from pExpr */ Fts5Expr *pNew = 0; /* Expression to return via *ppNew */ - TokenCtx sCtx = {0,0}; /* Context object for fts5ParseTokenize */ - - pOrig = pExpr->apExprPhrase[iPhrase]; - pNew = (Fts5Expr*)sqlite3Fts5MallocZero(&rc, sizeof(Fts5Expr)); + TokenCtx sCtx = {0,0,0}; /* Context object for fts5ParseTokenize */ + if( iPhrase<0 || iPhrase>=pExpr->nPhrase ){ + rc = SQLITE_RANGE; + }else{ + pOrig = pExpr->apExprPhrase[iPhrase]; + pNew = (Fts5Expr*)sqlite3Fts5MallocZero(&rc, sizeof(Fts5Expr)); + } if( rc==SQLITE_OK ){ pNew->apExprPhrase = (Fts5ExprPhrase**)sqlite3Fts5MallocZero(&rc, sizeof(Fts5ExprPhrase*)); @@ -232805,7 +236599,7 @@ static int sqlite3Fts5ExprClonePhrase( pNew->pRoot->pNear = (Fts5ExprNearset*)sqlite3Fts5MallocZero(&rc, sizeof(Fts5ExprNearset) + sizeof(Fts5ExprPhrase*)); } - if( rc==SQLITE_OK ){ + if( rc==SQLITE_OK && ALWAYS(pOrig!=0) ){ Fts5Colset *pColsetOrig = pOrig->pNode->pNear->pColset; if( pColsetOrig ){ sqlite3_int64 nByte; @@ -232819,26 +236613,27 @@ static int sqlite3Fts5ExprClonePhrase( } } - if( pOrig->nTerm ){ - int i; /* Used to iterate through phrase terms */ - for(i=0; rc==SQLITE_OK && inTerm; i++){ - int tflags = 0; - Fts5ExprTerm *p; - for(p=&pOrig->aTerm[i]; p && rc==SQLITE_OK; p=p->pSynonym){ - const char *zTerm = p->zTerm; - rc = fts5ParseTokenize((void*)&sCtx, tflags, zTerm, (int)strlen(zTerm), - 0, 0); - tflags = FTS5_TOKEN_COLOCATED; - } - if( rc==SQLITE_OK ){ - sCtx.pPhrase->aTerm[i].bPrefix = pOrig->aTerm[i].bPrefix; - sCtx.pPhrase->aTerm[i].bFirst = pOrig->aTerm[i].bFirst; + if( rc==SQLITE_OK ){ + if( pOrig->nTerm ){ + int i; /* Used to iterate through phrase terms */ + sCtx.pConfig = pExpr->pConfig; + for(i=0; rc==SQLITE_OK && inTerm; i++){ + int tflags = 0; + Fts5ExprTerm *p; + for(p=&pOrig->aTerm[i]; p && rc==SQLITE_OK; p=p->pSynonym){ + rc = fts5ParseTokenize((void*)&sCtx,tflags,p->pTerm,p->nFullTerm,0,0); + tflags = FTS5_TOKEN_COLOCATED; + } + if( rc==SQLITE_OK ){ + sCtx.pPhrase->aTerm[i].bPrefix = pOrig->aTerm[i].bPrefix; + sCtx.pPhrase->aTerm[i].bFirst = pOrig->aTerm[i].bFirst; + } } + }else{ + /* This happens when parsing a token or quoted phrase that contains + ** no token characters at all. (e.g ... MATCH '""'). */ + sCtx.pPhrase = sqlite3Fts5MallocZero(&rc, sizeof(Fts5ExprPhrase)); } - }else{ - /* This happens when parsing a token or quoted phrase that contains - ** no token characters at all. (e.g ... MATCH '""'). */ - sCtx.pPhrase = sqlite3Fts5MallocZero(&rc, sizeof(Fts5ExprPhrase)); } if( rc==SQLITE_OK && ALWAYS(sCtx.pPhrase) ){ @@ -233208,11 +237003,13 @@ static Fts5ExprNode *fts5ParsePhraseToAnd( if( parseGrowPhraseArray(pParse) ){ fts5ExprPhraseFree(pPhrase); }else{ + Fts5ExprTerm *p = &pNear->apPhrase[0]->aTerm[ii]; + Fts5ExprTerm *pTo = &pPhrase->aTerm[0]; pParse->apPhrase[pParse->nPhrase++] = pPhrase; pPhrase->nTerm = 1; - pPhrase->aTerm[0].zTerm = sqlite3Fts5Strndup( - &pParse->rc, pNear->apPhrase[0]->aTerm[ii].zTerm, -1 - ); + pTo->pTerm = sqlite3Fts5Strndup(&pParse->rc, p->pTerm, p->nFullTerm); + pTo->nQueryTerm = p->nQueryTerm; + pTo->nFullTerm = p->nFullTerm; pRet->apChild[ii] = sqlite3Fts5ParseNode(pParse, FTS5_STRING, 0, 0, sqlite3Fts5ParseNearset(pParse, 0, pPhrase) ); @@ -233397,16 +237194,17 @@ static char *fts5ExprTermPrint(Fts5ExprTerm *pTerm){ /* Determine the maximum amount of space required. */ for(p=pTerm; p; p=p->pSynonym){ - nByte += (int)strlen(pTerm->zTerm) * 2 + 3 + 2; + nByte += pTerm->nQueryTerm * 2 + 3 + 2; } zQuoted = sqlite3_malloc64(nByte); if( zQuoted ){ int i = 0; for(p=pTerm; p; p=p->pSynonym){ - char *zIn = p->zTerm; + char *zIn = p->pTerm; + char *zEnd = &zIn[p->nQueryTerm]; zQuoted[i++] = '"'; - while( *zIn ){ + while( zInnTerm; iTerm++){ - char *zTerm = pPhrase->aTerm[iTerm].zTerm; - zRet = fts5PrintfAppend(zRet, "%s%s", iTerm==0?"":" ", zTerm); + Fts5ExprTerm *p = &pPhrase->aTerm[iTerm]; + zRet = fts5PrintfAppend(zRet, "%s%.*s", iTerm==0?"":" ", + p->nQueryTerm, p->pTerm + ); if( pPhrase->aTerm[iTerm].bPrefix ){ zRet = fts5PrintfAppend(zRet, "*"); } @@ -233886,6 +237686,17 @@ static int fts5ExprColsetTest(Fts5Colset *pColset, int iCol){ return 0; } +/* +** pToken is a buffer nToken bytes in size that may or may not contain +** an embedded 0x00 byte. If it does, return the number of bytes in +** the buffer before the 0x00. If it does not, return nToken. +*/ +static int fts5QueryTerm(const char *pToken, int nToken){ + int ii; + for(ii=0; iipExpr; int i; + int nQuery = nToken; + i64 iRowid = pExpr->pRoot->iRowid; UNUSED_PARAM2(iUnused1, iUnused2); - if( nToken>FTS5_MAX_TOKEN_SIZE ) nToken = FTS5_MAX_TOKEN_SIZE; + if( nQuery>FTS5_MAX_TOKEN_SIZE ) nQuery = FTS5_MAX_TOKEN_SIZE; + if( pExpr->pConfig->bTokendata ){ + nQuery = fts5QueryTerm(pToken, nQuery); + } if( (tflags & FTS5_TOKEN_COLOCATED)==0 ) p->iOff++; for(i=0; inPhrase; i++){ - Fts5ExprTerm *pTerm; + Fts5ExprTerm *pT; if( p->aPopulator[i].bOk==0 ) continue; - for(pTerm=&pExpr->apExprPhrase[i]->aTerm[0]; pTerm; pTerm=pTerm->pSynonym){ - int nTerm = (int)strlen(pTerm->zTerm); - if( (nTerm==nToken || (nTermbPrefix)) - && memcmp(pTerm->zTerm, pToken, nTerm)==0 + for(pT=&pExpr->apExprPhrase[i]->aTerm[0]; pT; pT=pT->pSynonym){ + if( (pT->nQueryTerm==nQuery || (pT->nQueryTermbPrefix)) + && memcmp(pT->pTerm, pToken, pT->nQueryTerm)==0 ){ int rc = sqlite3Fts5PoslistWriterAppend( &pExpr->apExprPhrase[i]->poslist, &p->aPopulator[i].writer, p->iOff ); + if( rc==SQLITE_OK && pExpr->pConfig->bTokendata && !pT->bPrefix ){ + int iCol = p->iOff>>32; + int iTokOff = p->iOff & 0x7FFFFFFF; + rc = sqlite3Fts5IndexIterWriteTokendata( + pT->pIter, pToken, nToken, iRowid, iCol, iTokOff + ); + } if( rc ) return rc; break; } @@ -234048,6 +237870,83 @@ static int sqlite3Fts5ExprPhraseCollist( return rc; } +/* +** Does the work of the fts5_api.xQueryToken() API method. +*/ +static int sqlite3Fts5ExprQueryToken( + Fts5Expr *pExpr, + int iPhrase, + int iToken, + const char **ppOut, + int *pnOut +){ + Fts5ExprPhrase *pPhrase = 0; + + if( iPhrase<0 || iPhrase>=pExpr->nPhrase ){ + return SQLITE_RANGE; + } + pPhrase = pExpr->apExprPhrase[iPhrase]; + if( iToken<0 || iToken>=pPhrase->nTerm ){ + return SQLITE_RANGE; + } + + *ppOut = pPhrase->aTerm[iToken].pTerm; + *pnOut = pPhrase->aTerm[iToken].nFullTerm; + return SQLITE_OK; +} + +/* +** Does the work of the fts5_api.xInstToken() API method. +*/ +static int sqlite3Fts5ExprInstToken( + Fts5Expr *pExpr, + i64 iRowid, + int iPhrase, + int iCol, + int iOff, + int iToken, + const char **ppOut, + int *pnOut +){ + Fts5ExprPhrase *pPhrase = 0; + Fts5ExprTerm *pTerm = 0; + int rc = SQLITE_OK; + + if( iPhrase<0 || iPhrase>=pExpr->nPhrase ){ + return SQLITE_RANGE; + } + pPhrase = pExpr->apExprPhrase[iPhrase]; + if( iToken<0 || iToken>=pPhrase->nTerm ){ + return SQLITE_RANGE; + } + pTerm = &pPhrase->aTerm[iToken]; + if( pTerm->bPrefix==0 ){ + if( pExpr->pConfig->bTokendata ){ + rc = sqlite3Fts5IterToken( + pTerm->pIter, iRowid, iCol, iOff+iToken, ppOut, pnOut + ); + }else{ + *ppOut = pTerm->pTerm; + *pnOut = pTerm->nFullTerm; + } + } + return rc; +} + +/* +** Clear the token mappings for all Fts5IndexIter objects mannaged by +** the expression passed as the only argument. +*/ +static void sqlite3Fts5ExprClearTokens(Fts5Expr *pExpr){ + int ii; + for(ii=0; iinPhrase; ii++){ + Fts5ExprTerm *pT; + for(pT=&pExpr->apExprPhrase[ii]->aTerm[0]; pT; pT=pT->pSynonym){ + sqlite3Fts5IndexIterClearTokendata(pT->pIter); + } + } +} + /* ** 2014 August 11 ** @@ -234086,10 +237985,15 @@ struct Fts5Hash { /* ** Each entry in the hash table is represented by an object of the -** following type. Each object, its key (a nul-terminated string) and -** its current data are stored in a single memory allocation. The -** key immediately follows the object in memory. The position list -** data immediately follows the key data in memory. +** following type. Each object, its key, and its current data are stored +** in a single memory allocation. The key immediately follows the object +** in memory. The position list data immediately follows the key data +** in memory. +** +** The key is Fts5HashEntry.nKey bytes in size. It consists of a single +** byte identifying the index (either the main term index or a prefix-index), +** followed by the term data. For example: "0token". There is no +** nul-terminator - in this case nKey=6. ** ** The data that follows the key is in a similar, but not identical format ** to the doclist data stored in the database. It is: @@ -234224,8 +238128,7 @@ static int fts5HashResize(Fts5Hash *pHash){ unsigned int iHash; Fts5HashEntry *p = apOld[i]; apOld[i] = p->pHashNext; - iHash = fts5HashKey(nNew, (u8*)fts5EntryKey(p), - (int)strlen(fts5EntryKey(p))); + iHash = fts5HashKey(nNew, (u8*)fts5EntryKey(p), p->nKey); p->pHashNext = apNew[iHash]; apNew[iHash] = p; } @@ -234309,7 +238212,7 @@ static int sqlite3Fts5HashWrite( for(p=pHash->aSlot[iHash]; p; p=p->pHashNext){ char *zKey = fts5EntryKey(p); if( zKey[0]==bByte - && p->nKey==nToken + && p->nKey==nToken+1 && memcmp(&zKey[1], pToken, nToken)==0 ){ break; @@ -234339,9 +238242,9 @@ static int sqlite3Fts5HashWrite( zKey[0] = bByte; memcpy(&zKey[1], pToken, nToken); assert( iHash==fts5HashKey(pHash->nSlot, (u8*)zKey, nToken+1) ); - p->nKey = nToken; + p->nKey = nToken+1; zKey[nToken+1] = '\0'; - p->nData = nToken+1 + 1 + sizeof(Fts5HashEntry); + p->nData = nToken+1 + sizeof(Fts5HashEntry); p->pHashNext = pHash->aSlot[iHash]; pHash->aSlot[iHash] = p; pHash->nEntry++; @@ -234458,12 +238361,17 @@ static Fts5HashEntry *fts5HashEntryMerge( *ppOut = p1; p1 = 0; }else{ - int i = 0; char *zKey1 = fts5EntryKey(p1); char *zKey2 = fts5EntryKey(p2); - while( zKey1[i]==zKey2[i] ) i++; + int nMin = MIN(p1->nKey, p2->nKey); - if( ((u8)zKey1[i])>((u8)zKey2[i]) ){ + int cmp = memcmp(zKey1, zKey2, nMin); + if( cmp==0 ){ + cmp = p1->nKey - p2->nKey; + } + assert( cmp!=0 ); + + if( cmp>0 ){ /* p2 is smaller */ *ppOut = p2; ppOut = &p2->pScanNext; @@ -234482,10 +238390,8 @@ static Fts5HashEntry *fts5HashEntryMerge( } /* -** Extract all tokens from hash table iHash and link them into a list -** in sorted order. The hash table is cleared before returning. It is -** the responsibility of the caller to free the elements of the returned -** list. +** Link all tokens from hash table iHash into a list in sorted order. The +** tokens are not removed from the hash table. */ static int fts5HashEntrySort( Fts5Hash *pHash, @@ -234507,7 +238413,7 @@ static int fts5HashEntrySort( Fts5HashEntry *pIter; for(pIter=pHash->aSlot[iSlot]; pIter; pIter=pIter->pHashNext){ if( pTerm==0 - || (pIter->nKey+1>=nTerm && 0==memcmp(fts5EntryKey(pIter), pTerm, nTerm)) + || (pIter->nKey>=nTerm && 0==memcmp(fts5EntryKey(pIter), pTerm, nTerm)) ){ Fts5HashEntry *pEntry = pIter; pEntry->pScanNext = 0; @@ -234546,12 +238452,11 @@ static int sqlite3Fts5HashQuery( for(p=pHash->aSlot[iHash]; p; p=p->pHashNext){ zKey = fts5EntryKey(p); - assert( p->nKey+1==(int)strlen(zKey) ); - if( nTerm==p->nKey+1 && memcmp(zKey, pTerm, nTerm)==0 ) break; + if( nTerm==p->nKey && memcmp(zKey, pTerm, nTerm)==0 ) break; } if( p ){ - int nHashPre = sizeof(Fts5HashEntry) + nTerm + 1; + int nHashPre = sizeof(Fts5HashEntry) + nTerm; int nList = p->nData - nHashPre; u8 *pRet = (u8*)(*ppOut = sqlite3_malloc64(nPre + nList + 10)); if( pRet ){ @@ -234612,19 +238517,22 @@ static int sqlite3Fts5HashScanEof(Fts5Hash *p){ static void sqlite3Fts5HashScanEntry( Fts5Hash *pHash, const char **pzTerm, /* OUT: term (nul-terminated) */ + int *pnTerm, /* OUT: Size of term in bytes */ const u8 **ppDoclist, /* OUT: pointer to doclist */ int *pnDoclist /* OUT: size of doclist in bytes */ ){ Fts5HashEntry *p; if( (p = pHash->pScan) ){ char *zKey = fts5EntryKey(p); - int nTerm = (int)strlen(zKey); + int nTerm = p->nKey; fts5HashAddPoslistSize(pHash, p, 0); *pzTerm = zKey; - *ppDoclist = (const u8*)&zKey[nTerm+1]; - *pnDoclist = p->nData - (sizeof(Fts5HashEntry) + nTerm + 1); + *pnTerm = nTerm; + *ppDoclist = (const u8*)&zKey[nTerm]; + *pnDoclist = p->nData - (sizeof(Fts5HashEntry) + nTerm); }else{ *pzTerm = 0; + *pnTerm = 0; *ppDoclist = 0; *pnDoclist = 0; } @@ -234955,6 +238863,9 @@ typedef struct Fts5SegWriter Fts5SegWriter; typedef struct Fts5Structure Fts5Structure; typedef struct Fts5StructureLevel Fts5StructureLevel; typedef struct Fts5StructureSegment Fts5StructureSegment; +typedef struct Fts5TokenDataIter Fts5TokenDataIter; +typedef struct Fts5TokenDataMap Fts5TokenDataMap; +typedef struct Fts5TombstoneArray Fts5TombstoneArray; struct Fts5Data { u8 *p; /* Pointer to buffer containing record */ @@ -234989,6 +238900,7 @@ struct Fts5Index { /* Error state. */ int rc; /* Current error code */ + int flushRc; /* State used by the fts5DataXXX() functions. */ sqlite3_blob *pReader; /* RO incr-blob open on %_data table */ @@ -234997,6 +238909,7 @@ struct Fts5Index { sqlite3_stmt *pIdxWriter; /* "INSERT ... %_idx VALUES(?,?,?,?)" */ sqlite3_stmt *pIdxDeleter; /* "DELETE FROM %_idx WHERE segid=?" */ sqlite3_stmt *pIdxSelect; + sqlite3_stmt *pIdxNextSelect; int nRead; /* Total number of blocks read */ sqlite3_stmt *pDeleteFromIdx; @@ -235150,8 +239063,7 @@ struct Fts5SegIter { Fts5Data *pLeaf; /* Current leaf data */ Fts5Data *pNextLeaf; /* Leaf page (iLeafPgno+1) */ i64 iLeafOffset; /* Byte offset within current leaf */ - Fts5Data **apTombstone; /* Array of tombstone pages */ - int nTombstone; + Fts5TombstoneArray *pTombArray; /* Array of tombstone pages */ /* Next method */ void (*xNext)(Fts5Index*, Fts5SegIter*, int*); @@ -235178,6 +239090,15 @@ struct Fts5SegIter { u8 bDel; /* True if the delete flag is set */ }; +/* +** Array of tombstone pages. Reference counted. +*/ +struct Fts5TombstoneArray { + int nRef; /* Number of pointers to this object */ + int nTombstone; + Fts5Data *apTombstone[1]; /* Array of tombstone pages */ +}; + /* ** Argument is a pointer to an Fts5Data structure that contains a ** leaf page. @@ -235222,9 +239143,16 @@ struct Fts5SegIter { ** poslist: ** Used by sqlite3Fts5IterPoslist() when the poslist needs to be buffered. ** There is no way to tell if this is populated or not. +** +** pColset: +** If not NULL, points to an object containing a set of column indices. +** Only matches that occur in one of these columns will be returned. +** The Fts5Iter does not own the Fts5Colset object, and so it is not +** freed when the iterator is closed - it is owned by the upper layer. */ struct Fts5Iter { Fts5IndexIter base; /* Base class containing output vars */ + Fts5TokenDataIter *pTokenDataIter; Fts5Index *pIndex; /* Index that owns this iterator */ Fts5Buffer poslist; /* Buffer containing current poslist */ @@ -235242,7 +239170,6 @@ struct Fts5Iter { Fts5SegIter aSeg[1]; /* Array of segment iterators */ }; - /* ** An instance of the following type is used to iterate through the contents ** of a doclist-index record. @@ -236160,9 +240087,9 @@ static int fts5DlidxLvlNext(Fts5DlidxLvl *pLvl){ } if( iOffnn ){ - i64 iVal; + u64 iVal; pLvl->iLeafPgno += (iOff - pLvl->iOff) + 1; - iOff += fts5GetVarint(&pData->p[iOff], (u64*)&iVal); + iOff += fts5GetVarint(&pData->p[iOff], &iVal); pLvl->iRowid += iVal; pLvl->iOff = iOff; }else{ @@ -236541,18 +240468,20 @@ static void fts5SegIterSetNext(Fts5Index *p, Fts5SegIter *pIter){ } /* -** Allocate a tombstone hash page array (pIter->apTombstone) for the -** iterator passed as the second argument. If an OOM error occurs, leave -** an error in the Fts5Index object. +** Allocate a tombstone hash page array object (pIter->pTombArray) for +** the iterator passed as the second argument. If an OOM error occurs, +** leave an error in the Fts5Index object. */ static void fts5SegIterAllocTombstone(Fts5Index *p, Fts5SegIter *pIter){ const int nTomb = pIter->pSeg->nPgTombstone; if( nTomb>0 ){ - Fts5Data **apTomb = 0; - apTomb = (Fts5Data**)sqlite3Fts5MallocZero(&p->rc, sizeof(Fts5Data)*nTomb); - if( apTomb ){ - pIter->apTombstone = apTomb; - pIter->nTombstone = nTomb; + int nByte = nTomb * sizeof(Fts5Data*) + sizeof(Fts5TombstoneArray); + Fts5TombstoneArray *pNew; + pNew = (Fts5TombstoneArray*)sqlite3Fts5MallocZero(&p->rc, nByte); + if( pNew ){ + pNew->nTombstone = nTomb; + pNew->nRef = 1; + pIter->pTombArray = pNew; } } } @@ -236809,15 +240738,16 @@ static void fts5SegIterNext_None( }else{ const u8 *pList = 0; const char *zTerm = 0; + int nTerm = 0; int nList; sqlite3Fts5HashScanNext(p->pHash); - sqlite3Fts5HashScanEntry(p->pHash, &zTerm, &pList, &nList); + sqlite3Fts5HashScanEntry(p->pHash, &zTerm, &nTerm, &pList, &nList); if( pList==0 ) goto next_none_eof; pIter->pLeaf->p = (u8*)pList; pIter->pLeaf->nn = nList; pIter->pLeaf->szLeaf = nList; pIter->iEndofDoclist = nList; - sqlite3Fts5BufferSet(&p->rc,&pIter->term, (int)strlen(zTerm), (u8*)zTerm); + sqlite3Fts5BufferSet(&p->rc,&pIter->term, nTerm, (u8*)zTerm); pIter->iLeafOffset = fts5GetVarint(pList, (u64*)&pIter->iRowid); } @@ -236883,11 +240813,12 @@ static void fts5SegIterNext( }else if( pIter->pSeg==0 ){ const u8 *pList = 0; const char *zTerm = 0; + int nTerm = 0; int nList = 0; assert( (pIter->flags & FTS5_SEGITER_ONETERM) || pbNewTerm ); if( 0==(pIter->flags & FTS5_SEGITER_ONETERM) ){ sqlite3Fts5HashScanNext(p->pHash); - sqlite3Fts5HashScanEntry(p->pHash, &zTerm, &pList, &nList); + sqlite3Fts5HashScanEntry(p->pHash, &zTerm, &nTerm, &pList, &nList); } if( pList==0 ){ fts5DataRelease(pIter->pLeaf); @@ -236897,8 +240828,7 @@ static void fts5SegIterNext( pIter->pLeaf->nn = nList; pIter->pLeaf->szLeaf = nList; pIter->iEndofDoclist = nList+1; - sqlite3Fts5BufferSet(&p->rc, &pIter->term, (int)strlen(zTerm), - (u8*)zTerm); + sqlite3Fts5BufferSet(&p->rc, &pIter->term, nTerm, (u8*)zTerm); pIter->iLeafOffset = fts5GetVarint(pList, (u64*)&pIter->iRowid); *pbNewTerm = 1; } @@ -237284,7 +241214,7 @@ static void fts5SegIterSeekInit( fts5LeafSeek(p, bGe, pIter, pTerm, nTerm); } - if( p->rc==SQLITE_OK && bGe==0 ){ + if( p->rc==SQLITE_OK && (bGe==0 || (flags & FTS5INDEX_QUERY_SCANONETERM)) ){ pIter->flags |= FTS5_SEGITER_ONETERM; if( pIter->pLeaf ){ if( flags & FTS5INDEX_QUERY_DESC ){ @@ -237300,7 +241230,9 @@ static void fts5SegIterSeekInit( } fts5SegIterSetNext(p, pIter); - fts5SegIterAllocTombstone(p, pIter); + if( 0==(flags & FTS5INDEX_QUERY_SCANONETERM) ){ + fts5SegIterAllocTombstone(p, pIter); + } /* Either: ** @@ -237317,6 +241249,79 @@ static void fts5SegIterSeekInit( ); } + +/* +** SQL used by fts5SegIterNextInit() to find the page to open. +*/ +static sqlite3_stmt *fts5IdxNextStmt(Fts5Index *p){ + if( p->pIdxNextSelect==0 ){ + Fts5Config *pConfig = p->pConfig; + fts5IndexPrepareStmt(p, &p->pIdxNextSelect, sqlite3_mprintf( + "SELECT pgno FROM '%q'.'%q_idx' WHERE " + "segid=? AND term>? ORDER BY term ASC LIMIT 1", + pConfig->zDb, pConfig->zName + )); + + } + return p->pIdxNextSelect; +} + +/* +** This is similar to fts5SegIterSeekInit(), except that it initializes +** the segment iterator to point to the first term following the page +** with pToken/nToken on it. +*/ +static void fts5SegIterNextInit( + Fts5Index *p, + const char *pTerm, int nTerm, + Fts5StructureSegment *pSeg, /* Description of segment */ + Fts5SegIter *pIter /* Object to populate */ +){ + int iPg = -1; /* Page of segment to open */ + int bDlidx = 0; + sqlite3_stmt *pSel = 0; /* SELECT to find iPg */ + + pSel = fts5IdxNextStmt(p); + if( pSel ){ + assert( p->rc==SQLITE_OK ); + sqlite3_bind_int(pSel, 1, pSeg->iSegid); + sqlite3_bind_blob(pSel, 2, pTerm, nTerm, SQLITE_STATIC); + + if( sqlite3_step(pSel)==SQLITE_ROW ){ + i64 val = sqlite3_column_int64(pSel, 0); + iPg = (int)(val>>1); + bDlidx = (val & 0x0001); + } + p->rc = sqlite3_reset(pSel); + sqlite3_bind_null(pSel, 2); + if( p->rc ) return; + } + + memset(pIter, 0, sizeof(*pIter)); + pIter->pSeg = pSeg; + pIter->flags |= FTS5_SEGITER_ONETERM; + if( iPg>=0 ){ + pIter->iLeafPgno = iPg - 1; + fts5SegIterNextPage(p, pIter); + fts5SegIterSetNext(p, pIter); + } + if( pIter->pLeaf ){ + const u8 *a = pIter->pLeaf->p; + int iTermOff = 0; + + pIter->iPgidxOff = pIter->pLeaf->szLeaf; + pIter->iPgidxOff += fts5GetVarint32(&a[pIter->iPgidxOff], iTermOff); + pIter->iLeafOffset = iTermOff; + fts5SegIterLoadTerm(p, pIter, 0); + fts5SegIterLoadNPos(p, pIter); + if( bDlidx ) fts5SegIterLoadDlidx(p, pIter); + + assert( p->rc!=SQLITE_OK || + fts5BufferCompareBlob(&pIter->term, (const u8*)pTerm, nTerm)>0 + ); + } +} + /* ** Initialize the object pIter to point to term pTerm/nTerm within the ** in-memory hash table. If there is no such term in the hash-table, the @@ -237343,14 +241348,21 @@ static void fts5SegIterHashInit( const u8 *pList = 0; p->rc = sqlite3Fts5HashScanInit(p->pHash, (const char*)pTerm, nTerm); - sqlite3Fts5HashScanEntry(p->pHash, (const char**)&z, &pList, &nList); - n = (z ? (int)strlen((const char*)z) : 0); + sqlite3Fts5HashScanEntry(p->pHash, (const char**)&z, &n, &pList, &nList); if( pList ){ pLeaf = fts5IdxMalloc(p, sizeof(Fts5Data)); if( pLeaf ){ pLeaf->p = (u8*)pList; } } + + /* The call to sqlite3Fts5HashScanInit() causes the hash table to + ** fill the size field of all existing position lists. This means they + ** can no longer be appended to. Since the only scenario in which they + ** can be appended to is if the previous operation on this table was + ** a DELETE, by clearing the Fts5Index.bDelete flag we can avoid this + ** possibility altogether. */ + p->bDelete = 0; }else{ p->rc = sqlite3Fts5HashQuery(p->pHash, sizeof(Fts5Data), (const char*)pTerm, nTerm, (void**)&pLeaf, &nList @@ -237395,6 +241407,23 @@ static void fts5IndexFreeArray(Fts5Data **ap, int n){ } } +/* +** Decrement the ref-count of the object passed as the only argument. If it +** reaches 0, free it and its contents. +*/ +static void fts5TombstoneArrayDelete(Fts5TombstoneArray *p){ + if( p ){ + p->nRef--; + if( p->nRef<=0 ){ + int ii; + for(ii=0; iinTombstone; ii++){ + fts5DataRelease(p->apTombstone[ii]); + } + sqlite3_free(p); + } + } +} + /* ** Zero the iterator passed as the only argument. */ @@ -237402,7 +241431,7 @@ static void fts5SegIterClear(Fts5SegIter *pIter){ fts5BufferFree(&pIter->term); fts5DataRelease(pIter->pLeaf); fts5DataRelease(pIter->pNextLeaf); - fts5IndexFreeArray(pIter->apTombstone, pIter->nTombstone); + fts5TombstoneArrayDelete(pIter->pTombArray); fts5DlidxIterFree(pIter->pDlidx); sqlite3_free(pIter->aRowidOffset); memset(pIter, 0, sizeof(Fts5SegIter)); @@ -237536,7 +241565,6 @@ static int fts5MultiIterDoCompare(Fts5Iter *pIter, int iOut){ assert_nc( i2!=0 ); pRes->bTermEq = 1; if( p1->iRowid==p2->iRowid ){ - p1->bDel = p2->bDel; return i2; } res = ((p1->iRowid > p2->iRowid)==pIter->bRev) ? -1 : +1; @@ -237648,7 +241676,6 @@ static void fts5SegIterNextFrom( }while( p->rc==SQLITE_OK ); } - /* ** Free the iterator object passed as the second argument. */ @@ -237793,24 +241820,25 @@ static int fts5IndexTombstoneQuery( static int fts5MultiIterIsDeleted(Fts5Iter *pIter){ int iFirst = pIter->aFirst[1].iFirst; Fts5SegIter *pSeg = &pIter->aSeg[iFirst]; + Fts5TombstoneArray *pArray = pSeg->pTombArray; - if( pSeg->pLeaf && pSeg->nTombstone ){ + if( pSeg->pLeaf && pArray ){ /* Figure out which page the rowid might be present on. */ - int iPg = ((u64)pSeg->iRowid) % pSeg->nTombstone; + int iPg = ((u64)pSeg->iRowid) % pArray->nTombstone; assert( iPg>=0 ); /* If tombstone hash page iPg has not yet been loaded from the ** database, load it now. */ - if( pSeg->apTombstone[iPg]==0 ){ - pSeg->apTombstone[iPg] = fts5DataRead(pIter->pIndex, + if( pArray->apTombstone[iPg]==0 ){ + pArray->apTombstone[iPg] = fts5DataRead(pIter->pIndex, FTS5_TOMBSTONE_ROWID(pSeg->pSeg->iSegid, iPg) ); - if( pSeg->apTombstone[iPg]==0 ) return 0; + if( pArray->apTombstone[iPg]==0 ) return 0; } return fts5IndexTombstoneQuery( - pSeg->apTombstone[iPg], - pSeg->nTombstone, + pArray->apTombstone[iPg], + pArray->nTombstone, pSeg->iRowid ); } @@ -237904,7 +241932,7 @@ static Fts5Iter *fts5MultiIterAlloc( int nSeg ){ Fts5Iter *pNew; - int nSlot; /* Power of two >= nSeg */ + i64 nSlot; /* Power of two >= nSeg */ for(nSlot=2; nSlotnSeg-1; iIter>0; iIter--){ + int iEq; + if( (iEq = fts5MultiIterDoCompare(pIter, iIter)) ){ + Fts5SegIter *pSeg = &pIter->aSeg[iEq]; + if( p->rc==SQLITE_OK ) pSeg->xNext(p, pSeg, 0); + fts5MultiIterAdvanced(p, pIter, iEq, iIter); + } + } + fts5MultiIterSetEof(pIter); + fts5AssertMultiIterSetup(p, pIter); + + if( (pIter->bSkipEmpty && fts5MultiIterIsEmpty(p, pIter)) + || fts5MultiIterIsDeleted(pIter) + ){ + fts5MultiIterNext(p, pIter, 0, 0); + }else if( pIter->base.bEof==0 ){ + Fts5SegIter *pSeg = &pIter->aSeg[pIter->aFirst[1].iFirst]; + pIter->xSetOutputs(pIter, pSeg); + } +} /* ** Allocate a new Fts5Iter object. @@ -238430,31 +242484,12 @@ static void fts5MultiIterNew( assert( iIter==nSeg ); } - /* If the above was successful, each component iterators now points + /* If the above was successful, each component iterator now points ** to the first entry in its segment. In this case initialize the ** aFirst[] array. Or, if an error has occurred, free the iterator ** object and set the output variable to NULL. */ if( p->rc==SQLITE_OK ){ - for(iIter=pNew->nSeg-1; iIter>0; iIter--){ - int iEq; - if( (iEq = fts5MultiIterDoCompare(pNew, iIter)) ){ - Fts5SegIter *pSeg = &pNew->aSeg[iEq]; - if( p->rc==SQLITE_OK ) pSeg->xNext(p, pSeg, 0); - fts5MultiIterAdvanced(p, pNew, iEq, iIter); - } - } - fts5MultiIterSetEof(pNew); - fts5AssertMultiIterSetup(p, pNew); - - if( (pNew->bSkipEmpty && fts5MultiIterIsEmpty(p, pNew)) - || fts5MultiIterIsDeleted(pNew) - ){ - fts5MultiIterNext(p, pNew, 0, 0); - }else if( pNew->base.bEof==0 ){ - Fts5SegIter *pSeg = &pNew->aSeg[pNew->aFirst[1].iFirst]; - pNew->xSetOutputs(pNew, pSeg); - } - + fts5MultiIterFinishSetup(p, pNew); }else{ fts5MultiIterFree(pNew); *ppOut = 0; @@ -238479,7 +242514,6 @@ static void fts5MultiIterNew2( pNew = fts5MultiIterAlloc(p, 2); if( pNew ){ Fts5SegIter *pIter = &pNew->aSeg[1]; - pIter->flags = FTS5_SEGITER_ONETERM; if( pData->szLeaf>0 ){ pIter->pLeaf = pData; @@ -238627,6 +242661,7 @@ static void fts5IndexDiscardData(Fts5Index *p){ sqlite3Fts5HashClear(p->pHash); p->nPendingData = 0; p->nPendingRow = 0; + p->flushRc = SQLITE_OK; } p->nContentlessDelete = 0; } @@ -238842,7 +242877,7 @@ static void fts5WriteDlidxAppend( } if( pDlidx->bPrevValid ){ - iVal = iRowid - pDlidx->iPrev; + iVal = (u64)iRowid - (u64)pDlidx->iPrev; }else{ i64 iPgno = (i==0 ? pWriter->writer.pgno : pDlidx[-1].pgno); assert( pDlidx->buf.n==0 ); @@ -239029,7 +243064,7 @@ static void fts5WriteAppendPoslistData( const u8 *a = aData; int n = nData; - assert( p->pConfig->pgsz>0 ); + assert( p->pConfig->pgsz>0 || p->rc!=SQLITE_OK ); while( p->rc==SQLITE_OK && (pPage->buf.n + pPage->pgidx.n + n)>=p->pConfig->pgsz ){ @@ -239680,7 +243715,6 @@ static void fts5DoSecureDelete( int iPgIdx = pSeg->pLeaf->szLeaf; u64 iDelta = 0; - u64 iNextDelta = 0; int iNextOff = 0; int iOff = 0; int nIdx = 0; @@ -239688,8 +243722,6 @@ static void fts5DoSecureDelete( int bLastInDoclist = 0; int iIdx = 0; int iStart = 0; - int iKeyOff = 0; - int iPrevKeyOff = 0; int iDelKeyOff = 0; /* Offset of deleted key, if any */ nIdx = nPg-iPgIdx; @@ -239714,10 +243746,21 @@ static void fts5DoSecureDelete( ** This block sets the following variables: ** ** iStart: + ** The offset of the first byte of the rowid or delta-rowid + ** value for the doclist entry being removed. + ** ** iDelta: + ** The value of the rowid or delta-rowid value for the doclist + ** entry being removed. + ** + ** iNextOff: + ** The offset of the next entry following the position list + ** for the one being removed. If the position list for this + ** entry overflows onto the next leaf page, this value will be + ** greater than pLeaf->szLeaf. */ { - int iSOP; + int iSOP; /* Start-Of-Position-list */ if( pSeg->iLeafPgno==pSeg->iTermLeafPgno ){ iStart = pSeg->iTermLeafOffset; }else{ @@ -239753,47 +243796,81 @@ static void fts5DoSecureDelete( } iOff = iStart; + + /* If the position-list for the entry being removed flows over past + ** the end of this page, delete the portion of the position-list on the + ** next page and beyond. + ** + ** Set variable bLastInDoclist to true if this entry happens + ** to be the last rowid in the doclist for its term. */ if( iNextOff>=iPgIdx ){ int pgno = pSeg->iLeafPgno+1; fts5SecureDeleteOverflow(p, pSeg->pSeg, pgno, &bLastInDoclist); iNextOff = iPgIdx; - }else{ - /* Set bLastInDoclist to true if the entry being removed is the last - ** in its doclist. */ - for(iIdx=0, iKeyOff=0; iIdxbDel==0 ){ + if( iNextOff!=iPgIdx ){ + /* Loop through the page-footer. If iNextOff (offset of the + ** entry following the one we are removing) is equal to the + ** offset of a key on this page, then the entry is the last + ** in its doclist. */ + int iKeyOff = 0; + for(iIdx=0; iIdxbDel ){ + iOff += sqlite3Fts5PutVarint(&aPg[iOff], iDelta); + aPg[iOff++] = 0x01; + }else if( bLastInDoclist==0 ){ if( iNextOff!=iPgIdx ){ + u64 iNextDelta = 0; iNextOff += fts5GetVarint(&aPg[iNextOff], &iNextDelta); iOff += sqlite3Fts5PutVarint(&aPg[iOff], iDelta + iNextDelta); } }else if( - iStart==pSeg->iTermLeafOffset && pSeg->iLeafPgno==pSeg->iTermLeafPgno + pSeg->iLeafPgno==pSeg->iTermLeafPgno + && iStart==pSeg->iTermLeafOffset ){ /* The entry being removed was the only position list in its ** doclist. Therefore the term needs to be removed as well. */ int iKey = 0; - for(iIdx=0, iKeyOff=0; iIdx(u32)iStart ) break; iKeyOff += iVal; } + assert_nc( iKey>=1 ); + /* Set iDelKeyOff to the value of the footer entry to remove from + ** the page. */ iDelKeyOff = iOff = iKeyOff; + if( iNextOff!=iPgIdx ){ + /* This is the only position-list associated with the term, and there + ** is another term following it on this page. So the subsequent term + ** needs to be moved to replace the term associated with the entry + ** being removed. */ int nPrefix = 0; int nSuffix = 0; int nPrefix2 = 0; @@ -239872,6 +243949,15 @@ static void fts5DoSecureDelete( } } + /* Assuming no error has occurred, this block does final edits to the + ** leaf page before writing it back to disk. Input variables are: + ** + ** nPg: Total initial size of leaf page. + ** iPgIdx: Initial offset of page footer. + ** + ** iOff: Offset to move data to + ** iNextOff: Offset to move data from + */ if( p->rc==SQLITE_OK ){ const int nMove = nPg - iNextOff; /* Number of bytes to move */ int nShift = iNextOff - iOff; /* Distance to move them */ @@ -239914,10 +244000,10 @@ static void fts5FlushSecureDelete( Fts5Index *p, Fts5Structure *pStruct, const char *zTerm, + int nTerm, i64 iRowid ){ const int f = FTS5INDEX_QUERY_SKIPHASH; - int nTerm = (int)strlen(zTerm); Fts5Iter *pIter = 0; /* Used to find term instance */ fts5MultiIterNew(p, pStruct, f, 0, (const u8*)zTerm, nTerm, -1, 0, &pIter); @@ -239991,8 +244077,7 @@ static void fts5FlushOneHash(Fts5Index *p){ int nDoclist; /* Size of doclist in bytes */ /* Get the term and doclist for this entry. */ - sqlite3Fts5HashScanEntry(pHash, &zTerm, &pDoclist, &nDoclist); - nTerm = (int)strlen(zTerm); + sqlite3Fts5HashScanEntry(pHash, &zTerm, &nTerm, &pDoclist, &nDoclist); if( bSecureDelete==0 ){ fts5WriteAppendTerm(p, &writer, nTerm, (const u8*)zTerm); if( p->rc!=SQLITE_OK ) break; @@ -240022,7 +244107,7 @@ static void fts5FlushOneHash(Fts5Index *p){ if( bSecureDelete ){ if( eDetail==FTS5_DETAIL_NONE ){ if( iOffrc!=SQLITE_OK || pDoclist[iOff]==0x01 ){ iOff++; continue; @@ -240072,10 +244157,16 @@ static void fts5FlushOneHash(Fts5Index *p){ fts5WriteFlushLeaf(p, &writer); } }else{ - int bDummy; - int nPos; - int nCopy = fts5GetPoslistSize(&pDoclist[iOff], &nPos, &bDummy); - nCopy += nPos; + int bDel = 0; + int nPos = 0; + int nCopy = fts5GetPoslistSize(&pDoclist[iOff], &nPos, &bDel); + if( bDel && bSecureDelete ){ + fts5BufferAppendVarint(&p->rc, pBuf, nPos*2); + iOff += nCopy; + nCopy = nPos; + }else{ + nCopy += nPos; + } if( (pBuf->n + pPgidx->n + nCopy) <= pgsz ){ /* The entire poslist will fit on the current leaf. So copy ** it in one go. */ @@ -240113,7 +244204,6 @@ static void fts5FlushOneHash(Fts5Index *p){ assert( pBuf->n<=pBuf->nSpace ); if( p->rc==SQLITE_OK ) sqlite3Fts5HashScanNext(pHash); } - sqlite3Fts5HashClear(pHash); fts5WriteFinish(p, &writer, &pgnoLast); assert( p->rc!=SQLITE_OK || bSecureDelete || pgnoLast>0 ); @@ -240146,7 +244236,6 @@ static void fts5FlushOneHash(Fts5Index *p){ fts5IndexCrisismerge(p, &pStruct); fts5StructureWrite(p, pStruct); fts5StructureRelease(pStruct); - p->nContentlessDelete = 0; } /* @@ -240154,11 +244243,21 @@ static void fts5FlushOneHash(Fts5Index *p){ */ static void fts5IndexFlush(Fts5Index *p){ /* Unless it is empty, flush the hash table to disk */ + if( p->flushRc ){ + p->rc = p->flushRc; + return; + } if( p->nPendingData || p->nContentlessDelete ){ assert( p->pHash ); fts5FlushOneHash(p); - p->nPendingData = 0; - p->nPendingRow = 0; + if( p->rc==SQLITE_OK ){ + sqlite3Fts5HashClear(p->pHash); + p->nPendingData = 0; + p->nPendingRow = 0; + p->nContentlessDelete = 0; + }else if( p->nPendingData || p->nContentlessDelete ){ + p->flushRc = p->rc; + } } } @@ -240236,8 +244335,9 @@ static int sqlite3Fts5IndexOptimize(Fts5Index *p){ assert( p->rc==SQLITE_OK ); fts5IndexFlush(p); - assert( p->nContentlessDelete==0 ); + assert( p->rc!=SQLITE_OK || p->nContentlessDelete==0 ); pStruct = fts5StructureRead(p); + assert( p->rc!=SQLITE_OK || pStruct!=0 ); fts5StructureInvalidate(p); if( pStruct ){ @@ -240643,7 +244743,7 @@ static void fts5SetupPrefixIter( u8 *pToken, /* Buffer containing prefix to match */ int nToken, /* Size of buffer pToken in bytes */ Fts5Colset *pColset, /* Restrict matches to these columns */ - Fts5Iter **ppIter /* OUT: New iterator */ + Fts5Iter **ppIter /* OUT: New iterator */ ){ Fts5Structure *pStruct; Fts5Buffer *aBuf; @@ -240664,8 +244764,9 @@ static void fts5SetupPrefixIter( aBuf = (Fts5Buffer*)fts5IdxMalloc(p, sizeof(Fts5Buffer)*nBuf); pStruct = fts5StructureRead(p); + assert( p->rc!=SQLITE_OK || (aBuf && pStruct) ); - if( aBuf && pStruct ){ + if( p->rc==SQLITE_OK ){ const int flags = FTS5INDEX_QUERY_SCAN | FTS5INDEX_QUERY_SKIPEMPTY | FTS5INDEX_QUERY_NOOUTPUT; @@ -240677,6 +244778,12 @@ static void fts5SetupPrefixIter( int bNewTerm = 1; memset(&doclist, 0, sizeof(doclist)); + + /* If iIdx is non-zero, then it is the number of a prefix-index for + ** prefixes 1 character longer than the prefix being queried for. That + ** index contains all the doclists required, except for the one + ** corresponding to the prefix itself. That one is extracted from the + ** main term index here. */ if( iIdx!=0 ){ int dummy = 0; const int f2 = FTS5INDEX_QUERY_SKIPEMPTY|FTS5INDEX_QUERY_NOOUTPUT; @@ -240700,6 +244807,7 @@ static void fts5SetupPrefixIter( pToken[0] = FTS5_MAIN_PREFIX + iIdx; fts5MultiIterNew(p, pStruct, flags, pColset, pToken, nToken, -1, 0, &p1); fts5IterSetOutputCb(&p->rc, p1); + for( /* no-op */ ; fts5MultiIterEof(p, p1)==0; fts5MultiIterNext2(p, p1, &bNewTerm) @@ -240715,7 +244823,6 @@ static void fts5SetupPrefixIter( } if( p1->base.nData==0 ) continue; - if( p1->base.iRowid<=iLastRowid && doclist.n>0 ){ for(i=0; p->rc==SQLITE_OK && doclist.n; i++){ int i1 = i*nMerge; @@ -240754,7 +244861,7 @@ static void fts5SetupPrefixIter( } fts5MultiIterFree(p1); - pData = fts5IdxMalloc(p, sizeof(Fts5Data)+doclist.n+FTS5_DATA_ZERO_PADDING); + pData = fts5IdxMalloc(p, sizeof(*pData)+doclist.n+FTS5_DATA_ZERO_PADDING); if( pData ){ pData->p = (u8*)&pData[1]; pData->nn = pData->szLeaf = doclist.n; @@ -240897,6 +245004,7 @@ static int sqlite3Fts5IndexClose(Fts5Index *p){ sqlite3_finalize(p->pIdxWriter); sqlite3_finalize(p->pIdxDeleter); sqlite3_finalize(p->pIdxSelect); + sqlite3_finalize(p->pIdxNextSelect); sqlite3_finalize(p->pDataVersion); sqlite3_finalize(p->pDeleteFromIdx); sqlite3Fts5HashFree(p->pHash); @@ -240992,6 +245100,454 @@ static int sqlite3Fts5IndexWrite( return rc; } +/* +** pToken points to a buffer of size nToken bytes containing a search +** term, including the index number at the start, used on a tokendata=1 +** table. This function returns true if the term in buffer pBuf matches +** token pToken/nToken. +*/ +static int fts5IsTokendataPrefix( + Fts5Buffer *pBuf, + const u8 *pToken, + int nToken +){ + return ( + pBuf->n>=nToken + && 0==memcmp(pBuf->p, pToken, nToken) + && (pBuf->n==nToken || pBuf->p[nToken]==0x00) + ); +} + +/* +** Ensure the segment-iterator passed as the only argument points to EOF. +*/ +static void fts5SegIterSetEOF(Fts5SegIter *pSeg){ + fts5DataRelease(pSeg->pLeaf); + pSeg->pLeaf = 0; +} + +/* +** Usually, a tokendata=1 iterator (struct Fts5TokenDataIter) accumulates an +** array of these for each row it visits. Or, for an iterator used by an +** "ORDER BY rank" query, it accumulates an array of these for the entire +** query. +** +** Each instance in the array indicates the iterator (and therefore term) +** associated with position iPos of rowid iRowid. This is used by the +** xInstToken() API. +*/ +struct Fts5TokenDataMap { + i64 iRowid; /* Row this token is located in */ + i64 iPos; /* Position of token */ + int iIter; /* Iterator token was read from */ +}; + +/* +** An object used to supplement Fts5Iter for tokendata=1 iterators. +*/ +struct Fts5TokenDataIter { + int nIter; + int nIterAlloc; + + int nMap; + int nMapAlloc; + Fts5TokenDataMap *aMap; + + Fts5PoslistReader *aPoslistReader; + int *aPoslistToIter; + Fts5Iter *apIter[1]; +}; + +/* +** This function appends iterator pAppend to Fts5TokenDataIter pIn and +** returns the result. +*/ +static Fts5TokenDataIter *fts5AppendTokendataIter( + Fts5Index *p, /* Index object (for error code) */ + Fts5TokenDataIter *pIn, /* Current Fts5TokenDataIter struct */ + Fts5Iter *pAppend /* Append this iterator */ +){ + Fts5TokenDataIter *pRet = pIn; + + if( p->rc==SQLITE_OK ){ + if( pIn==0 || pIn->nIter==pIn->nIterAlloc ){ + int nAlloc = pIn ? pIn->nIterAlloc*2 : 16; + int nByte = nAlloc * sizeof(Fts5Iter*) + sizeof(Fts5TokenDataIter); + Fts5TokenDataIter *pNew = (Fts5TokenDataIter*)sqlite3_realloc(pIn, nByte); + + if( pNew==0 ){ + p->rc = SQLITE_NOMEM; + }else{ + if( pIn==0 ) memset(pNew, 0, nByte); + pRet = pNew; + pNew->nIterAlloc = nAlloc; + } + } + } + if( p->rc ){ + sqlite3Fts5IterClose((Fts5IndexIter*)pAppend); + }else{ + pRet->apIter[pRet->nIter++] = pAppend; + } + assert( pRet==0 || pRet->nIter<=pRet->nIterAlloc ); + + return pRet; +} + +/* +** Delete an Fts5TokenDataIter structure and its contents. +*/ +static void fts5TokendataIterDelete(Fts5TokenDataIter *pSet){ + if( pSet ){ + int ii; + for(ii=0; iinIter; ii++){ + fts5MultiIterFree(pSet->apIter[ii]); + } + sqlite3_free(pSet->aPoslistReader); + sqlite3_free(pSet->aMap); + sqlite3_free(pSet); + } +} + +/* +** Append a mapping to the token-map belonging to object pT. +*/ +static void fts5TokendataIterAppendMap( + Fts5Index *p, + Fts5TokenDataIter *pT, + int iIter, + i64 iRowid, + i64 iPos +){ + if( p->rc==SQLITE_OK ){ + if( pT->nMap==pT->nMapAlloc ){ + int nNew = pT->nMapAlloc ? pT->nMapAlloc*2 : 64; + int nByte = nNew * sizeof(Fts5TokenDataMap); + Fts5TokenDataMap *aNew; + + aNew = (Fts5TokenDataMap*)sqlite3_realloc(pT->aMap, nByte); + if( aNew==0 ){ + p->rc = SQLITE_NOMEM; + return; + } + + pT->aMap = aNew; + pT->nMapAlloc = nNew; + } + + pT->aMap[pT->nMap].iRowid = iRowid; + pT->aMap[pT->nMap].iPos = iPos; + pT->aMap[pT->nMap].iIter = iIter; + pT->nMap++; + } +} + +/* +** The iterator passed as the only argument must be a tokendata=1 iterator +** (pIter->pTokenDataIter!=0). This function sets the iterator output +** variables (pIter->base.*) according to the contents of the current +** row. +*/ +static void fts5IterSetOutputsTokendata(Fts5Iter *pIter){ + int ii; + int nHit = 0; + i64 iRowid = SMALLEST_INT64; + int iMin = 0; + + Fts5TokenDataIter *pT = pIter->pTokenDataIter; + + pIter->base.nData = 0; + pIter->base.pData = 0; + + for(ii=0; iinIter; ii++){ + Fts5Iter *p = pT->apIter[ii]; + if( p->base.bEof==0 ){ + if( nHit==0 || p->base.iRowidbase.iRowid; + nHit = 1; + pIter->base.pData = p->base.pData; + pIter->base.nData = p->base.nData; + iMin = ii; + }else if( p->base.iRowid==iRowid ){ + nHit++; + } + } + } + + if( nHit==0 ){ + pIter->base.bEof = 1; + }else{ + int eDetail = pIter->pIndex->pConfig->eDetail; + pIter->base.bEof = 0; + pIter->base.iRowid = iRowid; + + if( nHit==1 && eDetail==FTS5_DETAIL_FULL ){ + fts5TokendataIterAppendMap(pIter->pIndex, pT, iMin, iRowid, -1); + }else + if( nHit>1 && eDetail!=FTS5_DETAIL_NONE ){ + int nReader = 0; + int nByte = 0; + i64 iPrev = 0; + + /* Allocate array of iterators if they are not already allocated. */ + if( pT->aPoslistReader==0 ){ + pT->aPoslistReader = (Fts5PoslistReader*)sqlite3Fts5MallocZero( + &pIter->pIndex->rc, + pT->nIter * (sizeof(Fts5PoslistReader) + sizeof(int)) + ); + if( pT->aPoslistReader==0 ) return; + pT->aPoslistToIter = (int*)&pT->aPoslistReader[pT->nIter]; + } + + /* Populate an iterator for each poslist that will be merged */ + for(ii=0; iinIter; ii++){ + Fts5Iter *p = pT->apIter[ii]; + if( iRowid==p->base.iRowid ){ + pT->aPoslistToIter[nReader] = ii; + sqlite3Fts5PoslistReaderInit( + p->base.pData, p->base.nData, &pT->aPoslistReader[nReader++] + ); + nByte += p->base.nData; + } + } + + /* Ensure the output buffer is large enough */ + if( fts5BufferGrow(&pIter->pIndex->rc, &pIter->poslist, nByte+nHit*10) ){ + return; + } + + /* Ensure the token-mapping is large enough */ + if( eDetail==FTS5_DETAIL_FULL && pT->nMapAlloc<(pT->nMap + nByte) ){ + int nNew = (pT->nMapAlloc + nByte) * 2; + Fts5TokenDataMap *aNew = (Fts5TokenDataMap*)sqlite3_realloc( + pT->aMap, nNew*sizeof(Fts5TokenDataMap) + ); + if( aNew==0 ){ + pIter->pIndex->rc = SQLITE_NOMEM; + return; + } + pT->aMap = aNew; + pT->nMapAlloc = nNew; + } + + pIter->poslist.n = 0; + + while( 1 ){ + i64 iMinPos = LARGEST_INT64; + + /* Find smallest position */ + iMin = 0; + for(ii=0; iiaPoslistReader[ii]; + if( pReader->bEof==0 ){ + if( pReader->iPosiPos; + iMin = ii; + } + } + } + + /* If all readers were at EOF, break out of the loop. */ + if( iMinPos==LARGEST_INT64 ) break; + + sqlite3Fts5PoslistSafeAppend(&pIter->poslist, &iPrev, iMinPos); + sqlite3Fts5PoslistReaderNext(&pT->aPoslistReader[iMin]); + + if( eDetail==FTS5_DETAIL_FULL ){ + pT->aMap[pT->nMap].iPos = iMinPos; + pT->aMap[pT->nMap].iIter = pT->aPoslistToIter[iMin]; + pT->aMap[pT->nMap].iRowid = iRowid; + pT->nMap++; + } + } + + pIter->base.pData = pIter->poslist.p; + pIter->base.nData = pIter->poslist.n; + } + } +} + +/* +** The iterator passed as the only argument must be a tokendata=1 iterator +** (pIter->pTokenDataIter!=0). This function advances the iterator. If +** argument bFrom is false, then the iterator is advanced to the next +** entry. Or, if bFrom is true, it is advanced to the first entry with +** a rowid of iFrom or greater. +*/ +static void fts5TokendataIterNext(Fts5Iter *pIter, int bFrom, i64 iFrom){ + int ii; + Fts5TokenDataIter *pT = pIter->pTokenDataIter; + + for(ii=0; iinIter; ii++){ + Fts5Iter *p = pT->apIter[ii]; + if( p->base.bEof==0 + && (p->base.iRowid==pIter->base.iRowid || (bFrom && p->base.iRowidpIndex, p, bFrom, iFrom); + while( bFrom && p->base.bEof==0 + && p->base.iRowidpIndex->rc==SQLITE_OK + ){ + fts5MultiIterNext(p->pIndex, p, 0, 0); + } + } + } + + fts5IterSetOutputsTokendata(pIter); +} + +/* +** If the segment-iterator passed as the first argument is at EOF, then +** set pIter->term to a copy of buffer pTerm. +*/ +static void fts5TokendataSetTermIfEof(Fts5Iter *pIter, Fts5Buffer *pTerm){ + if( pIter && pIter->aSeg[0].pLeaf==0 ){ + fts5BufferSet(&pIter->pIndex->rc, &pIter->aSeg[0].term, pTerm->n, pTerm->p); + } +} + +/* +** This function sets up an iterator to use for a non-prefix query on a +** tokendata=1 table. +*/ +static Fts5Iter *fts5SetupTokendataIter( + Fts5Index *p, /* FTS index to query */ + const u8 *pToken, /* Buffer containing query term */ + int nToken, /* Size of buffer pToken in bytes */ + Fts5Colset *pColset /* Colset to filter on */ +){ + Fts5Iter *pRet = 0; + Fts5TokenDataIter *pSet = 0; + Fts5Structure *pStruct = 0; + const int flags = FTS5INDEX_QUERY_SCANONETERM | FTS5INDEX_QUERY_SCAN; + + Fts5Buffer bSeek = {0, 0, 0}; + Fts5Buffer *pSmall = 0; + + fts5IndexFlush(p); + pStruct = fts5StructureRead(p); + + while( p->rc==SQLITE_OK ){ + Fts5Iter *pPrev = pSet ? pSet->apIter[pSet->nIter-1] : 0; + Fts5Iter *pNew = 0; + Fts5SegIter *pNewIter = 0; + Fts5SegIter *pPrevIter = 0; + + int iLvl, iSeg, ii; + + pNew = fts5MultiIterAlloc(p, pStruct->nSegment); + if( pSmall ){ + fts5BufferSet(&p->rc, &bSeek, pSmall->n, pSmall->p); + fts5BufferAppendBlob(&p->rc, &bSeek, 1, (const u8*)"\0"); + }else{ + fts5BufferSet(&p->rc, &bSeek, nToken, pToken); + } + if( p->rc ){ + sqlite3Fts5IterClose((Fts5IndexIter*)pNew); + break; + } + + pNewIter = &pNew->aSeg[0]; + pPrevIter = (pPrev ? &pPrev->aSeg[0] : 0); + for(iLvl=0; iLvlnLevel; iLvl++){ + for(iSeg=pStruct->aLevel[iLvl].nSeg-1; iSeg>=0; iSeg--){ + Fts5StructureSegment *pSeg = &pStruct->aLevel[iLvl].aSeg[iSeg]; + int bDone = 0; + + if( pPrevIter ){ + if( fts5BufferCompare(pSmall, &pPrevIter->term) ){ + memcpy(pNewIter, pPrevIter, sizeof(Fts5SegIter)); + memset(pPrevIter, 0, sizeof(Fts5SegIter)); + bDone = 1; + }else if( pPrevIter->iEndofDoclist>pPrevIter->pLeaf->szLeaf ){ + fts5SegIterNextInit(p,(const char*)bSeek.p,bSeek.n-1,pSeg,pNewIter); + bDone = 1; + } + } + + if( bDone==0 ){ + fts5SegIterSeekInit(p, bSeek.p, bSeek.n, flags, pSeg, pNewIter); + } + + if( pPrevIter ){ + if( pPrevIter->pTombArray ){ + pNewIter->pTombArray = pPrevIter->pTombArray; + pNewIter->pTombArray->nRef++; + } + }else{ + fts5SegIterAllocTombstone(p, pNewIter); + } + + pNewIter++; + if( pPrevIter ) pPrevIter++; + if( p->rc ) break; + } + } + fts5TokendataSetTermIfEof(pPrev, pSmall); + + pNew->bSkipEmpty = 1; + pNew->pColset = pColset; + fts5IterSetOutputCb(&p->rc, pNew); + + /* Loop through all segments in the new iterator. Find the smallest + ** term that any segment-iterator points to. Iterator pNew will be + ** used for this term. Also, set any iterator that points to a term that + ** does not match pToken/nToken to point to EOF */ + pSmall = 0; + for(ii=0; iinSeg; ii++){ + Fts5SegIter *pII = &pNew->aSeg[ii]; + if( 0==fts5IsTokendataPrefix(&pII->term, pToken, nToken) ){ + fts5SegIterSetEOF(pII); + } + if( pII->pLeaf && (!pSmall || fts5BufferCompare(pSmall, &pII->term)>0) ){ + pSmall = &pII->term; + } + } + + /* If pSmall is still NULL at this point, then the new iterator does + ** not point to any terms that match the query. So delete it and break + ** out of the loop - all required iterators have been collected. */ + if( pSmall==0 ){ + sqlite3Fts5IterClose((Fts5IndexIter*)pNew); + break; + } + + /* Append this iterator to the set and continue. */ + pSet = fts5AppendTokendataIter(p, pSet, pNew); + } + + if( p->rc==SQLITE_OK && pSet ){ + int ii; + for(ii=0; iinIter; ii++){ + Fts5Iter *pIter = pSet->apIter[ii]; + int iSeg; + for(iSeg=0; iSegnSeg; iSeg++){ + pIter->aSeg[iSeg].flags |= FTS5_SEGITER_ONETERM; + } + fts5MultiIterFinishSetup(p, pIter); + } + } + + if( p->rc==SQLITE_OK ){ + pRet = fts5MultiIterAlloc(p, 0); + } + if( pRet ){ + pRet->pTokenDataIter = pSet; + if( pSet ){ + fts5IterSetOutputsTokendata(pRet); + }else{ + pRet->base.bEof = 1; + } + }else{ + fts5TokendataIterDelete(pSet); + } + + fts5StructureRelease(pStruct); + fts5BufferFree(&bSeek); + return pRet; +} + + /* ** Open a new iterator to iterate though all rowid that match the ** specified token or token prefix. @@ -241013,8 +245569,13 @@ static int sqlite3Fts5IndexQuery( if( sqlite3Fts5BufferSize(&p->rc, &buf, nToken+1)==0 ){ int iIdx = 0; /* Index to search */ int iPrefixIdx = 0; /* +1 prefix index */ + int bTokendata = pConfig->bTokendata; if( nToken>0 ) memcpy(&buf.p[1], pToken, nToken); + if( flags & (FTS5INDEX_QUERY_NOTOKENDATA|FTS5INDEX_QUERY_SCAN) ){ + bTokendata = 0; + } + /* Figure out which index to search and set iIdx accordingly. If this ** is a prefix query for which there is no prefix index, set iIdx to ** greater than pConfig->nPrefix to indicate that the query will be @@ -241040,7 +245601,10 @@ static int sqlite3Fts5IndexQuery( } } - if( iIdx<=pConfig->nPrefix ){ + if( bTokendata && iIdx==0 ){ + buf.p[0] = '0'; + pRet = fts5SetupTokendataIter(p, buf.p, nToken+1, pColset); + }else if( iIdx<=pConfig->nPrefix ){ /* Straight index lookup */ Fts5Structure *pStruct = fts5StructureRead(p); buf.p[0] = (u8)(FTS5_MAIN_PREFIX + iIdx); @@ -241087,7 +245651,11 @@ static int sqlite3Fts5IndexQuery( static int sqlite3Fts5IterNext(Fts5IndexIter *pIndexIter){ Fts5Iter *pIter = (Fts5Iter*)pIndexIter; assert( pIter->pIndex->rc==SQLITE_OK ); - fts5MultiIterNext(pIter->pIndex, pIter, 0, 0); + if( pIter->pTokenDataIter ){ + fts5TokendataIterNext(pIter, 0, 0); + }else{ + fts5MultiIterNext(pIter->pIndex, pIter, 0, 0); + } return fts5IndexReturn(pIter->pIndex); } @@ -241120,7 +245688,11 @@ static int sqlite3Fts5IterNextScan(Fts5IndexIter *pIndexIter){ */ static int sqlite3Fts5IterNextFrom(Fts5IndexIter *pIndexIter, i64 iMatch){ Fts5Iter *pIter = (Fts5Iter*)pIndexIter; - fts5MultiIterNextFrom(pIter->pIndex, pIter, iMatch); + if( pIter->pTokenDataIter ){ + fts5TokendataIterNext(pIter, 1, iMatch); + }else{ + fts5MultiIterNextFrom(pIter->pIndex, pIter, iMatch); + } return fts5IndexReturn(pIter->pIndex); } @@ -241135,6 +245707,99 @@ static const char *sqlite3Fts5IterTerm(Fts5IndexIter *pIndexIter, int *pn){ return (z ? &z[1] : 0); } +/* +** This is used by xInstToken() to access the token at offset iOff, column +** iCol of row iRowid. The token is returned via output variables *ppOut +** and *pnOut. The iterator passed as the first argument must be a tokendata=1 +** iterator (pIter->pTokenDataIter!=0). +*/ +static int sqlite3Fts5IterToken( + Fts5IndexIter *pIndexIter, + i64 iRowid, + int iCol, + int iOff, + const char **ppOut, int *pnOut +){ + Fts5Iter *pIter = (Fts5Iter*)pIndexIter; + Fts5TokenDataIter *pT = pIter->pTokenDataIter; + Fts5TokenDataMap *aMap = pT->aMap; + i64 iPos = (((i64)iCol)<<32) + iOff; + + int i1 = 0; + int i2 = pT->nMap; + int iTest = 0; + + while( i2>i1 ){ + iTest = (i1 + i2) / 2; + + if( aMap[iTest].iRowidiRowid ){ + i2 = iTest; + }else{ + if( aMap[iTest].iPosiPos ){ + i2 = iTest; + }else{ + break; + } + } + } + + if( i2>i1 ){ + Fts5Iter *pMap = pT->apIter[aMap[iTest].iIter]; + *ppOut = (const char*)pMap->aSeg[0].term.p+1; + *pnOut = pMap->aSeg[0].term.n-1; + } + + return SQLITE_OK; +} + +/* +** Clear any existing entries from the token-map associated with the +** iterator passed as the only argument. +*/ +static void sqlite3Fts5IndexIterClearTokendata(Fts5IndexIter *pIndexIter){ + Fts5Iter *pIter = (Fts5Iter*)pIndexIter; + if( pIter && pIter->pTokenDataIter ){ + pIter->pTokenDataIter->nMap = 0; + } +} + +/* +** Set a token-mapping for the iterator passed as the first argument. This +** is used in detail=column or detail=none mode when a token is requested +** using the xInstToken() API. In this case the caller tokenizers the +** current row and configures the token-mapping via multiple calls to this +** function. +*/ +static int sqlite3Fts5IndexIterWriteTokendata( + Fts5IndexIter *pIndexIter, + const char *pToken, int nToken, + i64 iRowid, int iCol, int iOff +){ + Fts5Iter *pIter = (Fts5Iter*)pIndexIter; + Fts5TokenDataIter *pT = pIter->pTokenDataIter; + Fts5Index *p = pIter->pIndex; + int ii; + + assert( p->pConfig->eDetail!=FTS5_DETAIL_FULL ); + assert( pIter->pTokenDataIter ); + + for(ii=0; iinIter; ii++){ + Fts5Buffer *pTerm = &pT->apIter[ii]->aSeg[0].term; + if( nToken==pTerm->n-1 && memcmp(pToken, pTerm->p+1, nToken)==0 ) break; + } + if( iinIter ){ + fts5TokendataIterAppendMap(p, pT, ii, iRowid, (((i64)iCol)<<32) + iOff); + } + return fts5IndexReturn(p); +} + /* ** Close an iterator opened by an earlier call to sqlite3Fts5IndexQuery(). */ @@ -241142,6 +245807,7 @@ static void sqlite3Fts5IterClose(Fts5IndexIter *pIndexIter){ if( pIndexIter ){ Fts5Iter *pIter = (Fts5Iter*)pIndexIter; Fts5Index *pIndex = pIter->pIndex; + fts5TokendataIterDelete(pIter->pTokenDataIter); fts5MultiIterFree(pIter); sqlite3Fts5IndexCloseReader(pIndex); } @@ -241649,7 +246315,9 @@ static int fts5QueryCksum( int eDetail = p->pConfig->eDetail; u64 cksum = *pCksum; Fts5IndexIter *pIter = 0; - int rc = sqlite3Fts5IndexQuery(p, z, n, flags, 0, &pIter); + int rc = sqlite3Fts5IndexQuery( + p, z, n, (flags | FTS5INDEX_QUERY_NOTOKENDATA), 0, &pIter + ); while( rc==SQLITE_OK && ALWAYS(pIter!=0) && 0==sqlite3Fts5IterEof(pIter) ){ i64 rowid = pIter->iRowid; @@ -241816,7 +246484,7 @@ static void fts5IndexIntegrityCheckEmpty( } static void fts5IntegrityCheckPgidx(Fts5Index *p, Fts5Data *pLeaf){ - int iTermOff = 0; + i64 iTermOff = 0; int ii; Fts5Buffer buf1 = {0,0,0}; @@ -241825,7 +246493,7 @@ static void fts5IntegrityCheckPgidx(Fts5Index *p, Fts5Data *pLeaf){ ii = pLeaf->szLeaf; while( iinn && p->rc==SQLITE_OK ){ int res; - int iOff; + i64 iOff; int nIncr; ii += fts5GetVarint32(&pLeaf->p[ii], nIncr); @@ -242347,6 +247015,24 @@ static void fts5DecodeRowidList( } #endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */ +#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG) +static void fts5BufferAppendTerm(int *pRc, Fts5Buffer *pBuf, Fts5Buffer *pTerm){ + int ii; + fts5BufferGrow(pRc, pBuf, pTerm->n*2 + 1); + if( *pRc==SQLITE_OK ){ + for(ii=0; iin; ii++){ + if( pTerm->p[ii]==0x00 ){ + pBuf->p[pBuf->n++] = '\\'; + pBuf->p[pBuf->n++] = '0'; + }else{ + pBuf->p[pBuf->n++] = pTerm->p[ii]; + } + } + pBuf->p[pBuf->n] = 0x00; + } +} +#endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */ + #if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG) /* ** The implementation of user-defined scalar function fts5_decode(). @@ -242454,9 +247140,8 @@ static void fts5DecodeFunction( iOff += fts5GetVarint32(&a[iOff], nAppend); term.n = nKeep; fts5BufferAppendBlob(&rc, &term, nAppend, &a[iOff]); - sqlite3Fts5BufferAppendPrintf( - &rc, &s, " term=%.*s", term.n, (const char*)term.p - ); + sqlite3Fts5BufferAppendPrintf(&rc, &s, " term="); + fts5BufferAppendTerm(&rc, &s, &term); iOff += nAppend; /* Figure out where the doclist for this term ends */ @@ -242564,9 +247249,8 @@ static void fts5DecodeFunction( fts5BufferAppendBlob(&rc, &term, nByte, &a[iOff]); iOff += nByte; - sqlite3Fts5BufferAppendPrintf( - &rc, &s, " term=%.*s", term.n, (const char*)term.p - ); + sqlite3Fts5BufferAppendPrintf(&rc, &s, " term="); + fts5BufferAppendTerm(&rc, &s, &term); iOff += fts5DecodeDoclist(&rc, &s, &a[iOff], iEnd-iOff); } @@ -242900,7 +247584,8 @@ static int sqlite3Fts5IndexInit(sqlite3 *db){ 0, /* xSavepoint */ 0, /* xRelease */ 0, /* xRollbackTo */ - 0 /* xShadowName */ + 0, /* xShadowName */ + 0 /* xIntegrity */ }; rc = sqlite3_create_module(db, "fts5_structure", &fts5structure_module, 0); } @@ -243039,6 +247724,8 @@ struct Fts5FullTable { Fts5Storage *pStorage; /* Document store */ Fts5Global *pGlobal; /* Global (connection wide) data */ Fts5Cursor *pSortCsr; /* Sort data from this cursor */ + int iSavepoint; /* Successful xSavepoint()+1 */ + #ifdef SQLITE_DEBUG struct Fts5TransactionState ts; #endif @@ -243327,6 +248014,13 @@ static int fts5InitVtab( pConfig->pzErrmsg = 0; } + if( rc==SQLITE_OK && pConfig->eContent==FTS5_CONTENT_NORMAL ){ + rc = sqlite3_vtab_config(db, SQLITE_VTAB_CONSTRAINT_SUPPORT, (int)1); + } + if( rc==SQLITE_OK ){ + rc = sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS); + } + if( rc!=SQLITE_OK ){ fts5FreeVtab(pTab); pTab = 0; @@ -243569,12 +248263,15 @@ static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){ } idxStr[iIdxStr] = '\0'; - /* Set idxFlags flags for the ORDER BY clause */ + /* Set idxFlags flags for the ORDER BY clause + ** + ** Note that tokendata=1 tables cannot currently handle "ORDER BY rowid DESC". + */ if( pInfo->nOrderBy==1 ){ int iSort = pInfo->aOrderBy[0].iColumn; if( iSort==(pConfig->nCol+1) && bSeenMatch ){ idxFlags |= FTS5_BI_ORDER_RANK; - }else if( iSort==-1 ){ + }else if( iSort==-1 && (!pInfo->aOrderBy[0].desc || !pConfig->bTokendata) ){ idxFlags |= FTS5_BI_ORDER_ROWID; } if( BitFlagTest(idxFlags, FTS5_BI_ORDER_RANK|FTS5_BI_ORDER_ROWID) ){ @@ -243826,6 +248523,16 @@ static int fts5NextMethod(sqlite3_vtab_cursor *pCursor){ ); assert( !CsrFlagTest(pCsr, FTS5CSR_EOF) ); + /* If this cursor uses FTS5_PLAN_MATCH and this is a tokendata=1 table, + ** clear any token mappings accumulated at the fts5_index.c level. In + ** other cases, specifically FTS5_PLAN_SOURCE and FTS5_PLAN_SORTED_MATCH, + ** we need to retain the mappings for the entire query. */ + if( pCsr->ePlan==FTS5_PLAN_MATCH + && ((Fts5Table*)pCursor->pVtab)->pConfig->bTokendata + ){ + sqlite3Fts5ExprClearTokens(pCsr->pExpr); + } + if( pCsr->ePlan<3 ){ int bSkip = 0; if( (rc = fts5CursorReseek(pCsr, &bSkip)) || bSkip ) return rc; @@ -244251,6 +248958,9 @@ static int fts5FilterMethod( pCsr->iFirstRowid = fts5GetRowidLimit(pRowidGe, SMALLEST_INT64); } + rc = sqlite3Fts5IndexLoadConfig(pTab->p.pIndex); + if( rc!=SQLITE_OK ) goto filter_out; + if( pTab->pSortCsr ){ /* If pSortCsr is non-NULL, then this call is being made as part of ** processing for a "... MATCH ORDER BY rank" query (ePlan is @@ -244273,6 +248983,7 @@ static int fts5FilterMethod( pCsr->pExpr = pTab->pSortCsr->pExpr; rc = fts5CursorFirst(pTab, pCsr, bDesc); }else if( pCsr->pExpr ){ + assert( rc==SQLITE_OK ); rc = fts5CursorParseRank(pConfig, pCsr, pRank); if( rc==SQLITE_OK ){ if( bOrderByRank ){ @@ -244444,6 +249155,7 @@ static int fts5SpecialInsert( Fts5Config *pConfig = pTab->p.pConfig; int rc = SQLITE_OK; int bError = 0; + int bLoadConfig = 0; if( 0==sqlite3_stricmp("delete-all", zCmd) ){ if( pConfig->eContent==FTS5_CONTENT_NORMAL ){ @@ -244455,6 +249167,7 @@ static int fts5SpecialInsert( }else{ rc = sqlite3Fts5StorageDeleteAll(pTab->pStorage); } + bLoadConfig = 1; }else if( 0==sqlite3_stricmp("rebuild", zCmd) ){ if( pConfig->eContent==FTS5_CONTENT_NONE ){ fts5SetVtabError(pTab, @@ -244464,6 +249177,7 @@ static int fts5SpecialInsert( }else{ rc = sqlite3Fts5StorageRebuild(pTab->pStorage); } + bLoadConfig = 1; }else if( 0==sqlite3_stricmp("optimize", zCmd) ){ rc = sqlite3Fts5StorageOptimize(pTab->pStorage); }else if( 0==sqlite3_stricmp("merge", zCmd) ){ @@ -244476,8 +249190,13 @@ static int fts5SpecialInsert( }else if( 0==sqlite3_stricmp("prefix-index", zCmd) ){ pConfig->bPrefixIndex = sqlite3_value_int(pVal); #endif + }else if( 0==sqlite3_stricmp("flush", zCmd) ){ + rc = sqlite3Fts5FlushToDisk(&pTab->p); }else{ - rc = sqlite3Fts5IndexLoadConfig(pTab->p.pIndex); + rc = sqlite3Fts5FlushToDisk(&pTab->p); + if( rc==SQLITE_OK ){ + rc = sqlite3Fts5IndexLoadConfig(pTab->p.pIndex); + } if( rc==SQLITE_OK ){ rc = sqlite3Fts5ConfigSetValue(pTab->p.pConfig, zCmd, pVal, &bError); } @@ -244489,6 +249208,12 @@ static int fts5SpecialInsert( } } } + + if( rc==SQLITE_OK && bLoadConfig ){ + pTab->p.pConfig->iCookie--; + rc = sqlite3Fts5IndexLoadConfig(pTab->p.pIndex); + } + return rc; } @@ -244607,7 +249332,7 @@ static int fts5UpdateMethod( assert( nArg!=1 || eType0==SQLITE_INTEGER ); /* Filter out attempts to run UPDATE or DELETE on contentless tables. - ** This is not suported. Except - DELETE is supported if the CREATE + ** This is not suported. Except - they are both supported if the CREATE ** VIRTUAL TABLE statement contained "contentless_delete=1". */ if( eType0==SQLITE_INTEGER && pConfig->eContent==FTS5_CONTENT_NONE @@ -244636,7 +249361,8 @@ static int fts5UpdateMethod( } else if( eType0!=SQLITE_INTEGER ){ - /* If this is a REPLACE, first remove the current entry (if any) */ + /* An INSERT statement. If the conflict-mode is REPLACE, first remove + ** the current entry (if any). */ if( eConflict==SQLITE_REPLACE && eType1==SQLITE_INTEGER ){ i64 iNew = sqlite3_value_int64(apVal[1]); /* Rowid to delete */ rc = sqlite3Fts5StorageDelete(pTab->pStorage, iNew, 0); @@ -244795,7 +249521,10 @@ static int fts5ApiColumnText( ){ int rc = SQLITE_OK; Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; - if( fts5IsContentless((Fts5FullTable*)(pCsr->base.pVtab)) + Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab); + if( iCol<0 || iCol>=pTab->pConfig->nCol ){ + rc = SQLITE_RANGE; + }else if( fts5IsContentless((Fts5FullTable*)(pCsr->base.pVtab)) || pCsr->ePlan==FTS5_PLAN_SPECIAL ){ *pz = 0; @@ -244820,8 +249549,9 @@ static int fts5CsrPoslist( int rc = SQLITE_OK; int bLive = (pCsr->pSorter==0); - if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_POSLIST) ){ - + if( iPhrase<0 || iPhrase>=sqlite3Fts5ExprPhraseCount(pCsr->pExpr) ){ + rc = SQLITE_RANGE; + }else if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_POSLIST) ){ if( pConfig->eDetail!=FTS5_DETAIL_FULL ){ Fts5PoslistPopulator *aPopulator; int i; @@ -244845,15 +249575,21 @@ static int fts5CsrPoslist( CsrFlagClear(pCsr, FTS5CSR_REQUIRE_POSLIST); } - if( pCsr->pSorter && pConfig->eDetail==FTS5_DETAIL_FULL ){ - Fts5Sorter *pSorter = pCsr->pSorter; - int i1 = (iPhrase==0 ? 0 : pSorter->aIdx[iPhrase-1]); - *pn = pSorter->aIdx[iPhrase] - i1; - *pa = &pSorter->aPoslist[i1]; + if( rc==SQLITE_OK ){ + if( pCsr->pSorter && pConfig->eDetail==FTS5_DETAIL_FULL ){ + Fts5Sorter *pSorter = pCsr->pSorter; + int i1 = (iPhrase==0 ? 0 : pSorter->aIdx[iPhrase-1]); + *pn = pSorter->aIdx[iPhrase] - i1; + *pa = &pSorter->aPoslist[i1]; + }else{ + *pn = sqlite3Fts5ExprPoslist(pCsr->pExpr, iPhrase, pa); + } }else{ - *pn = sqlite3Fts5ExprPoslist(pCsr->pExpr, iPhrase, pa); + *pa = 0; + *pn = 0; } + return rc; } @@ -244960,12 +249696,6 @@ static int fts5ApiInst( ){ if( iIdx<0 || iIdx>=pCsr->nInstCount ){ rc = SQLITE_RANGE; -#if 0 - }else if( fts5IsOffsetless((Fts5Table*)pCsr->base.pVtab) ){ - *piPhrase = pCsr->aInst[iIdx*3]; - *piCol = pCsr->aInst[iIdx*3 + 2]; - *piOff = -1; -#endif }else{ *piPhrase = pCsr->aInst[iIdx*3]; *piCol = pCsr->aInst[iIdx*3 + 1]; @@ -245220,13 +249950,56 @@ static int fts5ApiPhraseFirstColumn( return rc; } +/* +** xQueryToken() API implemenetation. +*/ +static int fts5ApiQueryToken( + Fts5Context* pCtx, + int iPhrase, + int iToken, + const char **ppOut, + int *pnOut +){ + Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; + return sqlite3Fts5ExprQueryToken(pCsr->pExpr, iPhrase, iToken, ppOut, pnOut); +} + +/* +** xInstToken() API implemenetation. +*/ +static int fts5ApiInstToken( + Fts5Context *pCtx, + int iIdx, + int iToken, + const char **ppOut, int *pnOut +){ + Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; + int rc = SQLITE_OK; + if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_INST)==0 + || SQLITE_OK==(rc = fts5CacheInstArray(pCsr)) + ){ + if( iIdx<0 || iIdx>=pCsr->nInstCount ){ + rc = SQLITE_RANGE; + }else{ + int iPhrase = pCsr->aInst[iIdx*3]; + int iCol = pCsr->aInst[iIdx*3 + 1]; + int iOff = pCsr->aInst[iIdx*3 + 2]; + i64 iRowid = fts5CursorRowid(pCsr); + rc = sqlite3Fts5ExprInstToken( + pCsr->pExpr, iRowid, iPhrase, iCol, iOff, iToken, ppOut, pnOut + ); + } + } + return rc; +} + static int fts5ApiQueryPhrase(Fts5Context*, int, void*, int(*)(const Fts5ExtensionApi*, Fts5Context*, void*) ); static const Fts5ExtensionApi sFts5Api = { - 2, /* iVersion */ + 3, /* iVersion */ fts5ApiUserData, fts5ApiColumnCount, fts5ApiRowCount, @@ -245246,6 +250019,8 @@ static const Fts5ExtensionApi sFts5Api = { fts5ApiPhraseNext, fts5ApiPhraseFirstColumn, fts5ApiPhraseNextColumn, + fts5ApiQueryToken, + fts5ApiInstToken }; /* @@ -245510,8 +250285,10 @@ static int fts5RenameMethod( sqlite3_vtab *pVtab, /* Virtual table handle */ const char *zName /* New name of table */ ){ + int rc; Fts5FullTable *pTab = (Fts5FullTable*)pVtab; - return sqlite3Fts5StorageRename(pTab->pStorage, zName); + rc = sqlite3Fts5StorageRename(pTab->pStorage, zName); + return rc; } static int sqlite3Fts5FlushToDisk(Fts5Table *pTab){ @@ -245525,9 +250302,15 @@ static int sqlite3Fts5FlushToDisk(Fts5Table *pTab){ ** Flush the contents of the pending-terms table to disk. */ static int fts5SavepointMethod(sqlite3_vtab *pVtab, int iSavepoint){ - UNUSED_PARAM(iSavepoint); /* Call below is a no-op for NDEBUG builds */ - fts5CheckTransactionState((Fts5FullTable*)pVtab, FTS5_SAVEPOINT, iSavepoint); - return sqlite3Fts5FlushToDisk((Fts5Table*)pVtab); + Fts5FullTable *pTab = (Fts5FullTable*)pVtab; + int rc = SQLITE_OK; + + fts5CheckTransactionState(pTab, FTS5_SAVEPOINT, iSavepoint); + rc = sqlite3Fts5FlushToDisk((Fts5Table*)pVtab); + if( rc==SQLITE_OK ){ + pTab->iSavepoint = iSavepoint+1; + } + return rc; } /* @@ -245536,9 +250319,16 @@ static int fts5SavepointMethod(sqlite3_vtab *pVtab, int iSavepoint){ ** This is a no-op. */ static int fts5ReleaseMethod(sqlite3_vtab *pVtab, int iSavepoint){ - UNUSED_PARAM(iSavepoint); /* Call below is a no-op for NDEBUG builds */ - fts5CheckTransactionState((Fts5FullTable*)pVtab, FTS5_RELEASE, iSavepoint); - return sqlite3Fts5FlushToDisk((Fts5Table*)pVtab); + Fts5FullTable *pTab = (Fts5FullTable*)pVtab; + int rc = SQLITE_OK; + fts5CheckTransactionState(pTab, FTS5_RELEASE, iSavepoint); + if( (iSavepoint+1)iSavepoint ){ + rc = sqlite3Fts5FlushToDisk(&pTab->p); + if( rc==SQLITE_OK ){ + pTab->iSavepoint = iSavepoint; + } + } + return rc; } /* @@ -245548,11 +250338,14 @@ static int fts5ReleaseMethod(sqlite3_vtab *pVtab, int iSavepoint){ */ static int fts5RollbackToMethod(sqlite3_vtab *pVtab, int iSavepoint){ Fts5FullTable *pTab = (Fts5FullTable*)pVtab; - UNUSED_PARAM(iSavepoint); /* Call below is a no-op for NDEBUG builds */ + int rc = SQLITE_OK; fts5CheckTransactionState(pTab, FTS5_ROLLBACKTO, iSavepoint); fts5TripCursors(pTab); - pTab->p.pConfig->pgsz = 0; - return sqlite3Fts5StorageRollback(pTab->pStorage); + if( (iSavepoint+1)<=pTab->iSavepoint ){ + pTab->p.pConfig->pgsz = 0; + rc = sqlite3Fts5StorageRollback(pTab->pStorage); + } + return rc; } /* @@ -245754,7 +250547,7 @@ static void fts5SourceIdFunc( ){ assert( nArg==0 ); UNUSED_PARAM2(nArg, apUnused); - sqlite3_result_text(pCtx, "fts5: 2023-09-11 12:01:27 2d3a40c05c49e1a49264912b1a05bc2143ac0e7c3df588276ce80a4cbc9bd1b0", -1, SQLITE_TRANSIENT); + sqlite3_result_text(pCtx, "fts5: 2024-01-30 16:01:20 e876e51a0ed5c5b3126f52e532044363a014bc594cfefa87ffb5b82257cc467a", -1, SQLITE_TRANSIENT); } /* @@ -245772,9 +250565,40 @@ static int fts5ShadowName(const char *zName){ return 0; } +/* +** Run an integrity check on the FTS5 data structures. Return a string +** if anything is found amiss. Return a NULL pointer if everything is +** OK. +*/ +static int fts5IntegrityMethod( + sqlite3_vtab *pVtab, /* the FTS5 virtual table to check */ + const char *zSchema, /* Name of schema in which this table lives */ + const char *zTabname, /* Name of the table itself */ + int isQuick, /* True if this is a quick-check */ + char **pzErr /* Write error message here */ +){ + Fts5FullTable *pTab = (Fts5FullTable*)pVtab; + int rc; + + assert( pzErr!=0 && *pzErr==0 ); + UNUSED_PARAM(isQuick); + rc = sqlite3Fts5StorageIntegrity(pTab->pStorage, 0); + if( (rc&0xff)==SQLITE_CORRUPT ){ + *pzErr = sqlite3_mprintf("malformed inverted index for FTS5 table %s.%s", + zSchema, zTabname); + }else if( rc!=SQLITE_OK ){ + *pzErr = sqlite3_mprintf("unable to validate the inverted index for" + " FTS5 table %s.%s: %s", + zSchema, zTabname, sqlite3_errstr(rc)); + } + sqlite3Fts5IndexCloseReader(pTab->p.pIndex); + + return SQLITE_OK; +} + static int fts5Init(sqlite3 *db){ static const sqlite3_module fts5Mod = { - /* iVersion */ 3, + /* iVersion */ 4, /* xCreate */ fts5CreateMethod, /* xConnect */ fts5ConnectMethod, /* xBestIndex */ fts5BestIndexMethod, @@ -245797,7 +250621,8 @@ static int fts5Init(sqlite3 *db){ /* xSavepoint */ fts5SavepointMethod, /* xRelease */ fts5ReleaseMethod, /* xRollbackTo */ fts5RollbackToMethod, - /* xShadowName */ fts5ShadowName + /* xShadowName */ fts5ShadowName, + /* xIntegrity */ fts5IntegrityMethod }; int rc; @@ -246563,7 +251388,7 @@ static int sqlite3Fts5StorageRebuild(Fts5Storage *p){ } if( rc==SQLITE_OK ){ - rc = fts5StorageGetStmt(p, FTS5_STMT_SCAN, &pScan, 0); + rc = fts5StorageGetStmt(p, FTS5_STMT_SCAN, &pScan, pConfig->pzErrmsg); } while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pScan) ){ @@ -247074,7 +251899,9 @@ static int sqlite3Fts5StorageSync(Fts5Storage *p){ i64 iLastRowid = sqlite3_last_insert_rowid(p->pConfig->db); if( p->bTotalsValid ){ rc = fts5StorageSaveTotals(p); - p->bTotalsValid = 0; + if( rc==SQLITE_OK ){ + p->bTotalsValid = 0; + } } if( rc==SQLITE_OK ){ rc = sqlite3Fts5IndexSync(p->pIndex); @@ -247348,6 +252175,12 @@ static const unsigned char sqlite3Utf8Trans1[] = { #endif /* ifndef SQLITE_AMALGAMATION */ +#define FTS5_SKIP_UTF8(zIn) { \ + if( ((unsigned char)(*(zIn++)))>=0xc0 ){ \ + while( (((unsigned char)*zIn) & 0xc0)==0x80 ){ zIn++; } \ + } \ +} + typedef struct Unicode61Tokenizer Unicode61Tokenizer; struct Unicode61Tokenizer { unsigned char aTokenChar[128]; /* ASCII range token characters */ @@ -248383,6 +253216,7 @@ static int fts5PorterTokenize( typedef struct TrigramTokenizer TrigramTokenizer; struct TrigramTokenizer { int bFold; /* True to fold to lower-case */ + int iFoldParam; /* Parameter to pass to Fts5UnicodeFold() */ }; /* @@ -248409,6 +253243,7 @@ static int fts5TriCreate( }else{ int i; pNew->bFold = 1; + pNew->iFoldParam = 0; for(i=0; rc==SQLITE_OK && ibFold = (zArg[0]=='0'); } + }else if( 0==sqlite3_stricmp(azArg[i], "remove_diacritics") ){ + if( (zArg[0]!='0' && zArg[0]!='1' && zArg[0]!='2') || zArg[1] ){ + rc = SQLITE_ERROR; + }else{ + pNew->iFoldParam = (zArg[0]!='0') ? 2 : 0; + } }else{ rc = SQLITE_ERROR; } } + + if( pNew->iFoldParam!=0 && pNew->bFold==0 ){ + rc = SQLITE_ERROR; + } + if( rc!=SQLITE_OK ){ fts5TriDelete((Fts5Tokenizer*)pNew); pNew = 0; @@ -248443,40 +253289,62 @@ static int fts5TriTokenize( TrigramTokenizer *p = (TrigramTokenizer*)pTok; int rc = SQLITE_OK; char aBuf[32]; + char *zOut = aBuf; + int ii; const unsigned char *zIn = (const unsigned char*)pText; const unsigned char *zEof = &zIn[nText]; u32 iCode; + int aStart[3]; /* Input offset of each character in aBuf[] */ UNUSED_PARAM(unusedFlags); - while( 1 ){ - char *zOut = aBuf; - int iStart = zIn - (const unsigned char*)pText; - const unsigned char *zNext; - READ_UTF8(zIn, zEof, iCode); - if( iCode==0 ) break; - zNext = zIn; - if( zInbFold ) iCode = sqlite3Fts5UnicodeFold(iCode, 0); - WRITE_UTF8(zOut, iCode); + /* Populate aBuf[] with the characters for the first trigram. */ + for(ii=0; ii<3; ii++){ + do { + aStart[ii] = zIn - (const unsigned char*)pText; + READ_UTF8(zIn, zEof, iCode); + if( iCode==0 ) return SQLITE_OK; + if( p->bFold ) iCode = sqlite3Fts5UnicodeFold(iCode, p->iFoldParam); + }while( iCode==0 ); + WRITE_UTF8(zOut, iCode); + } + + /* At the start of each iteration of this loop: + ** + ** aBuf: Contains 3 characters. The 3 characters of the next trigram. + ** zOut: Points to the byte following the last character in aBuf. + ** aStart[3]: Contains the byte offset in the input text corresponding + ** to the start of each of the three characters in the buffer. + */ + assert( zIn<=zEof ); + while( 1 ){ + int iNext; /* Start of character following current tri */ + const char *z1; + + /* Read characters from the input up until the first non-diacritic */ + do { + iNext = zIn - (const unsigned char*)pText; READ_UTF8(zIn, zEof, iCode); if( iCode==0 ) break; - }else{ - break; - } - if( zInbFold ) iCode = sqlite3Fts5UnicodeFold(iCode, 0); - WRITE_UTF8(zOut, iCode); - READ_UTF8(zIn, zEof, iCode); - if( iCode==0 ) break; - if( p->bFold ) iCode = sqlite3Fts5UnicodeFold(iCode, 0); - WRITE_UTF8(zOut, iCode); - }else{ - break; - } - rc = xToken(pCtx, 0, aBuf, zOut-aBuf, iStart, iStart + zOut-aBuf); - if( rc!=SQLITE_OK ) break; - zIn = zNext; + if( p->bFold ) iCode = sqlite3Fts5UnicodeFold(iCode, p->iFoldParam); + }while( iCode==0 ); + + /* Pass the current trigram back to fts5 */ + rc = xToken(pCtx, 0, aBuf, zOut-aBuf, aStart[0], iNext); + if( iCode==0 || rc!=SQLITE_OK ) break; + + /* Remove the first character from buffer aBuf[]. Append the character + ** with codepoint iCode. */ + z1 = aBuf; + FTS5_SKIP_UTF8(z1); + memmove(aBuf, z1, zOut - z1); + zOut -= (z1 - aBuf); + WRITE_UTF8(zOut, iCode); + + /* Update the aStart[] array */ + aStart[0] = aStart[1]; + aStart[1] = aStart[2]; + aStart[2] = iNext; } return rc; @@ -248499,7 +253367,9 @@ static int sqlite3Fts5TokenizerPattern( ){ if( xCreate==fts5TriCreate ){ TrigramTokenizer *p = (TrigramTokenizer*)pTok; - return p->bFold ? FTS5_PATTERN_LIKE : FTS5_PATTERN_GLOB; + if( p->iFoldParam==0 ){ + return p->bFold ? FTS5_PATTERN_LIKE : FTS5_PATTERN_GLOB; + } } return FTS5_PATTERN_NONE; } @@ -250288,7 +255158,7 @@ static int fts5VocabFilterMethod( if( pEq ){ zTerm = (const char *)sqlite3_value_text(pEq); nTerm = sqlite3_value_bytes(pEq); - f = 0; + f = FTS5INDEX_QUERY_NOTOKENDATA; }else{ if( pGe ){ zTerm = (const char *)sqlite3_value_text(pGe); @@ -250442,7 +255312,8 @@ static int sqlite3Fts5VocabInit(Fts5Global *pGlobal, sqlite3 *db){ /* xSavepoint */ 0, /* xRelease */ 0, /* xRollbackTo */ 0, - /* xShadowName */ 0 + /* xShadowName */ 0, + /* xIntegrity */ 0 }; void *p = (void*)pGlobal; @@ -250771,6 +255642,7 @@ static sqlite3_module stmtModule = { 0, /* xRelease */ 0, /* xRollbackTo */ 0, /* xShadowName */ + 0 /* xIntegrity */ }; #endif /* SQLITE_OMIT_VIRTUALTABLE */ diff --git a/src/libs/3rdparty/sqlite/sqlite3.h b/src/libs/3rdparty/sqlite/sqlite3.h index b9d06929888..4fdfde004eb 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.43.1" -#define SQLITE_VERSION_NUMBER 3043001 -#define SQLITE_SOURCE_ID "2023-09-11 12:01:27 2d3a40c05c49e1a49264912b1a05bc2143ac0e7c3df588276ce80a4cbc9bd1b0" +#define SQLITE_VERSION "3.45.1" +#define SQLITE_VERSION_NUMBER 3045001 +#define SQLITE_SOURCE_ID "2024-01-30 16:01:20 e876e51a0ed5c5b3126f52e532044363a014bc594cfefa87ffb5b82257cc467a" /* ** CAPI3REF: Run-Time Library Version Numbers @@ -2127,7 +2127,7 @@ struct sqlite3_mem_methods { ** is stored in each sorted record and the required column values loaded ** from the database as records are returned in sorted order. The default ** value for this option is to never use this optimization. Specifying a -** negative value for this option restores the default behaviour. +** negative value for this option restores the default behavior. ** This option is only available if SQLite is compiled with the ** [SQLITE_ENABLE_SORTER_REFERENCES] compile-time option. ** @@ -2302,7 +2302,7 @@ struct sqlite3_mem_methods { ** database handle, SQLite checks if this will mean that there are now no ** connections at all to the database. If so, it performs a checkpoint ** operation before closing the connection. This option may be used to -** override this behaviour. The first parameter passed to this operation +** override this behavior. The first parameter passed to this operation ** is an integer - positive to disable checkpoints-on-close, or zero (the ** default) to enable them, and negative to leave the setting unchanged. ** The second parameter is a pointer to an integer @@ -3954,14 +3954,17 @@ SQLITE_API void sqlite3_free_filename(sqlite3_filename); ** ** ** ^The sqlite3_errmsg() and sqlite3_errmsg16() return English-language -** text that describes the error, as either UTF-8 or UTF-16 respectively. +** text that describes the error, as either UTF-8 or UTF-16 respectively, +** or NULL if no error message is available. +** (See how SQLite handles [invalid UTF] for exceptions to this rule.) ** ^(Memory to hold the error message string is managed internally. ** The application does not need to worry about freeing the result. ** However, the error string might be overwritten or deallocated by ** subsequent calls to other SQLite interface functions.)^ ** -** ^The sqlite3_errstr() interface returns the English-language text -** that describes the [result code], as UTF-8. +** ^The sqlite3_errstr(E) interface returns the English-language text +** that describes the [result code] E, as UTF-8, or NULL if E is not an +** result code for which a text error message is available. ** ^(Memory to hold the error message string is managed internally ** and must not be freed by the application)^. ** @@ -5325,6 +5328,7 @@ SQLITE_API int sqlite3_finalize(sqlite3_stmt *pStmt); */ SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt); + /* ** CAPI3REF: Create Or Redefine SQL Functions ** KEYWORDS: {function creation routines} @@ -5571,13 +5575,27 @@ SQLITE_API int sqlite3_create_window_function( **
    ** ** [[SQLITE_SUBTYPE]]
    SQLITE_SUBTYPE
    -** The SQLITE_SUBTYPE flag indicates to SQLite that a function may call +** The SQLITE_SUBTYPE flag indicates to SQLite that a function might call ** [sqlite3_value_subtype()] to inspect the sub-types of its arguments. -** Specifying this flag makes no difference for scalar or aggregate user -** functions. However, if it is not specified for a user-defined window -** function, then any sub-types belonging to arguments passed to the window -** function may be discarded before the window function is called (i.e. -** sqlite3_value_subtype() will always return 0). +** This flag instructs SQLite to omit some corner-case optimizations that +** might disrupt the operation of the [sqlite3_value_subtype()] function, +** causing it to return zero rather than the correct subtype(). +** SQL functions that invokes [sqlite3_value_subtype()] should have this +** property. If the SQLITE_SUBTYPE property is omitted, then the return +** value from [sqlite3_value_subtype()] might sometimes be zero even though +** a non-zero subtype was specified by the function argument expression. +** +** [[SQLITE_RESULT_SUBTYPE]]
    SQLITE_RESULT_SUBTYPE
    +** The SQLITE_RESULT_SUBTYPE flag indicates to SQLite that a function might call +** [sqlite3_result_subtype()] to cause a sub-type to be associated with its +** result. +** Every function that invokes [sqlite3_result_subtype()] should have this +** property. If it does not, then the call to [sqlite3_result_subtype()] +** might become a no-op if the function is used as term in an +** [expression index]. On the other hand, SQL functions that never invoke +** [sqlite3_result_subtype()] should avoid setting this property, as the +** purpose of this property is to disable certain optimizations that are +** incompatible with subtypes. **
    ** */ @@ -5585,6 +5603,7 @@ SQLITE_API int sqlite3_create_window_function( #define SQLITE_DIRECTONLY 0x000080000 #define SQLITE_SUBTYPE 0x000100000 #define SQLITE_INNOCUOUS 0x000200000 +#define SQLITE_RESULT_SUBTYPE 0x001000000 /* ** CAPI3REF: Deprecated Functions @@ -5781,6 +5800,12 @@ SQLITE_API int sqlite3_value_encoding(sqlite3_value*); ** information can be used to pass a limited amount of context from ** one SQL function to another. Use the [sqlite3_result_subtype()] ** routine to set the subtype for the return value of an SQL function. +** +** Every [application-defined SQL function] that invoke this interface +** should include the [SQLITE_SUBTYPE] property in the text +** encoding argument when the function is [sqlite3_create_function|registered]. +** If the [SQLITE_SUBTYPE] property is omitted, then sqlite3_value_subtype() +** might return zero instead of the upstream subtype in some corner cases. */ SQLITE_API unsigned int sqlite3_value_subtype(sqlite3_value*); @@ -5879,48 +5904,56 @@ SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context*); ** METHOD: sqlite3_context ** ** These functions may be used by (non-aggregate) SQL functions to -** associate metadata with argument values. If the same value is passed to -** multiple invocations of the same SQL function during query execution, under -** some circumstances the associated metadata may be preserved. An example -** of where this might be useful is in a regular-expression matching -** function. The compiled version of the regular expression can be stored as -** metadata associated with the pattern string. +** associate auxiliary data with argument values. If the same argument +** value is passed to multiple invocations of the same SQL function during +** query execution, under some circumstances the associated auxiliary data +** might be preserved. An example of where this might be useful is in a +** regular-expression matching function. The compiled version of the regular +** expression can be stored as auxiliary data associated with the pattern string. ** Then as long as the pattern string remains the same, ** the compiled regular expression can be reused on multiple ** invocations of the same function. ** -** ^The sqlite3_get_auxdata(C,N) interface returns a pointer to the metadata +** ^The sqlite3_get_auxdata(C,N) interface returns a pointer to the auxiliary data ** associated by the sqlite3_set_auxdata(C,N,P,X) function with the Nth argument ** value to the application-defined function. ^N is zero for the left-most -** function argument. ^If there is no metadata +** function argument. ^If there is no auxiliary data ** associated with the function argument, the sqlite3_get_auxdata(C,N) interface ** returns a NULL pointer. ** -** ^The sqlite3_set_auxdata(C,N,P,X) interface saves P as metadata for the N-th -** argument of the application-defined function. ^Subsequent +** ^The sqlite3_set_auxdata(C,N,P,X) interface saves P as auxiliary data for the +** N-th argument of the application-defined function. ^Subsequent ** calls to sqlite3_get_auxdata(C,N) return P from the most recent -** sqlite3_set_auxdata(C,N,P,X) call if the metadata is still valid or -** NULL if the metadata has been discarded. +** sqlite3_set_auxdata(C,N,P,X) call if the auxiliary data is still valid or +** NULL if the auxiliary data has been discarded. ** ^After each call to sqlite3_set_auxdata(C,N,P,X) where X is not NULL, ** SQLite will invoke the destructor function X with parameter P exactly -** once, when the metadata is discarded. -** SQLite is free to discard the metadata at any time, including:
      +** once, when the auxiliary data is discarded. +** SQLite is free to discard the auxiliary data at any time, including:
        **
      • ^(when the corresponding function parameter changes)^, or **
      • ^(when [sqlite3_reset()] or [sqlite3_finalize()] is called for the ** SQL statement)^, or **
      • ^(when sqlite3_set_auxdata() is invoked again on the same ** parameter)^, or **
      • ^(during the original sqlite3_set_auxdata() call when a memory -** allocation error occurs.)^
      +** allocation error occurs.)^ +**
    • ^(during the original sqlite3_set_auxdata() call if the function +** is evaluated during query planning instead of during query execution, +** as sometimes happens with [SQLITE_ENABLE_STAT4].)^
    ** -** Note the last bullet in particular. The destructor X in +** Note the last two bullets in particular. The destructor X in ** sqlite3_set_auxdata(C,N,P,X) might be called immediately, before the ** sqlite3_set_auxdata() interface even returns. Hence sqlite3_set_auxdata() ** should be called near the end of the function implementation and the ** function implementation should not make any use of P after -** sqlite3_set_auxdata() has been called. +** sqlite3_set_auxdata() has been called. Furthermore, a call to +** sqlite3_get_auxdata() that occurs immediately after a corresponding call +** to sqlite3_set_auxdata() might still return NULL if an out-of-memory +** condition occurred during the sqlite3_set_auxdata() call or if the +** function is being evaluated during query planning rather than during +** query execution. ** -** ^(In practice, metadata is preserved between function calls for +** ^(In practice, auxiliary data is preserved between function calls for ** function parameters that are compile-time constants, including literal ** values and [parameters] and expressions composed from the same.)^ ** @@ -5930,10 +5963,67 @@ SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context*); ** ** These routines must be called from the same thread in which ** the SQL function is running. +** +** See also: [sqlite3_get_clientdata()] and [sqlite3_set_clientdata()]. */ SQLITE_API void *sqlite3_get_auxdata(sqlite3_context*, int N); SQLITE_API void sqlite3_set_auxdata(sqlite3_context*, int N, void*, void (*)(void*)); +/* +** CAPI3REF: Database Connection Client Data +** METHOD: sqlite3 +** +** These functions are used to associate one or more named pointers +** with a [database connection]. +** A call to sqlite3_set_clientdata(D,N,P,X) causes the pointer P +** to be attached to [database connection] D using name N. Subsequent +** calls to sqlite3_get_clientdata(D,N) will return a copy of pointer P +** or a NULL pointer if there were no prior calls to +** sqlite3_set_clientdata() with the same values of D and N. +** Names are compared using strcmp() and are thus case sensitive. +** +** If P and X are both non-NULL, then the destructor X is invoked with +** argument P on the first of the following occurrences: +**
      +**
    • An out-of-memory error occurs during the call to +** sqlite3_set_clientdata() which attempts to register pointer P. +**
    • A subsequent call to sqlite3_set_clientdata(D,N,P,X) is made +** with the same D and N parameters. +**
    • The database connection closes. SQLite does not make any guarantees +** about the order in which destructors are called, only that all +** destructors will be called exactly once at some point during the +** database connection closing process. +**
    +** +** SQLite does not do anything with client data other than invoke +** destructors on the client data at the appropriate time. The intended +** use for client data is to provide a mechanism for wrapper libraries +** to store additional information about an SQLite database connection. +** +** There is no limit (other than available memory) on the number of different +** client data pointers (with different names) that can be attached to a +** single database connection. However, the implementation is optimized +** for the case of having only one or two different client data names. +** Applications and wrapper libraries are discouraged from using more than +** one client data name each. +** +** There is no way to enumerate the client data pointers +** associated with a database connection. The N parameter can be thought +** of as a secret key such that only code that knows the secret key is able +** to access the associated data. +** +** Security Warning: These interfaces should not be exposed in scripting +** languages or in other circumstances where it might be possible for an +** an attacker to invoke them. Any agent that can invoke these interfaces +** can probably also take control of the process. +** +** Database connection client data is only available for SQLite +** version 3.44.0 ([dateof:3.44.0]) and later. +** +** See also: [sqlite3_set_auxdata()] and [sqlite3_get_auxdata()]. +*/ +SQLITE_API void *sqlite3_get_clientdata(sqlite3*,const char*); +SQLITE_API int sqlite3_set_clientdata(sqlite3*, const char*, void*, void(*)(void*)); /* ** CAPI3REF: Constants Defining Special Destructor Behavior @@ -6135,6 +6225,20 @@ SQLITE_API int sqlite3_result_zeroblob64(sqlite3_context*, sqlite3_uint64 n); ** higher order bits are discarded. ** The number of subtype bytes preserved by SQLite might increase ** in future releases of SQLite. +** +** Every [application-defined SQL function] that invokes this interface +** should include the [SQLITE_RESULT_SUBTYPE] property in its +** text encoding argument when the SQL function is +** [sqlite3_create_function|registered]. If the [SQLITE_RESULT_SUBTYPE] +** property is omitted from the function that invokes sqlite3_result_subtype(), +** then in some cases the sqlite3_result_subtype() might fail to set +** the result subtype. +** +** If SQLite is compiled with -DSQLITE_STRICT_SUBTYPE=1, then any +** SQL function that invokes the sqlite3_result_subtype() interface +** and that does not have the SQLITE_RESULT_SUBTYPE property will raise +** an error. Future versions of SQLite might enable -DSQLITE_STRICT_SUBTYPE=1 +** by default. */ SQLITE_API void sqlite3_result_subtype(sqlite3_context*,unsigned int); @@ -6566,7 +6670,7 @@ SQLITE_API int sqlite3_db_readonly(sqlite3 *db, const char *zDbName); SQLITE_API int sqlite3_txn_state(sqlite3*,const char *zSchema); /* -** CAPI3REF: Allowed return values from [sqlite3_txn_state()] +** CAPI3REF: Allowed return values from sqlite3_txn_state() ** KEYWORDS: {transaction state} ** ** These constants define the current transaction state of a database file. @@ -6698,7 +6802,7 @@ SQLITE_API void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*); ** ^Each call to the sqlite3_autovacuum_pages() interface overrides all ** previous invocations for that database connection. ^If the callback ** argument (C) to sqlite3_autovacuum_pages(D,C,P,X) is a NULL pointer, -** then the autovacuum steps callback is cancelled. The return value +** then the autovacuum steps callback is canceled. The return value ** from sqlite3_autovacuum_pages() is normally SQLITE_OK, but might ** be some other error code if something goes wrong. The current ** implementation will only return SQLITE_OK or SQLITE_MISUSE, but other @@ -7217,6 +7321,10 @@ struct sqlite3_module { /* The methods above are in versions 1 and 2 of the sqlite_module object. ** Those below are for version 3 and greater. */ int (*xShadowName)(const char*); + /* The methods above are in versions 1 through 3 of the sqlite_module object. + ** Those below are for version 4 and greater. */ + int (*xIntegrity)(sqlite3_vtab *pVTab, const char *zSchema, + const char *zTabName, int mFlags, char **pzErr); }; /* @@ -7704,7 +7812,7 @@ SQLITE_API int sqlite3_blob_reopen(sqlite3_blob *, sqlite3_int64); ** code is returned and the transaction rolled back. ** ** Calling this function with an argument that is not a NULL pointer or an -** open blob handle results in undefined behaviour. ^Calling this routine +** open blob handle results in undefined behavior. ^Calling this routine ** with a null pointer (such as would be returned by a failed call to ** [sqlite3_blob_open()]) is a harmless no-op. ^Otherwise, if this function ** is passed a valid open blob handle, the values returned by the @@ -7931,9 +8039,11 @@ SQLITE_API int sqlite3_vfs_unregister(sqlite3_vfs*); ** ** ^(Some systems (for example, Windows 95) do not support the operation ** implemented by sqlite3_mutex_try(). On those systems, sqlite3_mutex_try() -** will always return SQLITE_BUSY. The SQLite core only ever uses -** sqlite3_mutex_try() as an optimization so this is acceptable -** behavior.)^ +** will always return SQLITE_BUSY. In most cases the SQLite core only uses +** sqlite3_mutex_try() as an optimization, so this is acceptable +** behavior. The exceptions are unix builds that set the +** SQLITE_ENABLE_SETLK_TIMEOUT build option. In that case a working +** sqlite3_mutex_try() is required.)^ ** ** ^The sqlite3_mutex_leave() routine exits a mutex that was ** previously entered by the same thread. The behavior @@ -8184,6 +8294,7 @@ SQLITE_API int sqlite3_test_control(int op, ...); #define SQLITE_TESTCTRL_PRNG_SAVE 5 #define SQLITE_TESTCTRL_PRNG_RESTORE 6 #define SQLITE_TESTCTRL_PRNG_RESET 7 /* NOT USED */ +#define SQLITE_TESTCTRL_FK_NO_ACTION 7 #define SQLITE_TESTCTRL_BITVEC_TEST 8 #define SQLITE_TESTCTRL_FAULT_INSTALL 9 #define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10 @@ -8191,6 +8302,7 @@ SQLITE_API int sqlite3_test_control(int op, ...); #define SQLITE_TESTCTRL_ASSERT 12 #define SQLITE_TESTCTRL_ALWAYS 13 #define SQLITE_TESTCTRL_RESERVE 14 /* NOT USED */ +#define SQLITE_TESTCTRL_JSON_SELFCHECK 14 #define SQLITE_TESTCTRL_OPTIMIZATIONS 15 #define SQLITE_TESTCTRL_ISKEYWORD 16 /* NOT USED */ #define SQLITE_TESTCTRL_SCRATCHMALLOC 17 /* NOT USED */ @@ -9245,8 +9357,8 @@ SQLITE_API int sqlite3_backup_pagecount(sqlite3_backup *p); ** blocked connection already has a registered unlock-notify callback, ** then the new callback replaces the old.)^ ^If sqlite3_unlock_notify() is ** called with a NULL pointer as its second argument, then any existing -** unlock-notify callback is cancelled. ^The blocked connections -** unlock-notify callback may also be cancelled by closing the blocked +** unlock-notify callback is canceled. ^The blocked connections +** unlock-notify callback may also be canceled by closing the blocked ** connection using [sqlite3_close()]. ** ** The unlock-notify callback is not reentrant. If an application invokes @@ -10549,6 +10661,13 @@ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_recover(sqlite3 *db, const c ** SQLITE_SERIALIZE_NOCOPY bit is set but no contiguous copy ** of the database exists. ** +** After the call, if the SQLITE_SERIALIZE_NOCOPY bit had been set, +** the returned buffer content will remain accessible and unchanged +** until either the next write operation on the connection or when +** the connection is closed, and applications must not modify the +** buffer. If the bit had been clear, the returned buffer will not +** be accessed by SQLite after the call. +** ** A call to sqlite3_serialize(D,S,P,F) might return NULL even if the ** SQLITE_SERIALIZE_NOCOPY bit is omitted from argument F if a memory ** allocation error occurs. @@ -10597,6 +10716,9 @@ SQLITE_API unsigned char *sqlite3_serialize( ** SQLite will try to increase the buffer size using sqlite3_realloc64() ** if writes on the database cause it to grow larger than M bytes. ** +** Applications must not modify the buffer P or invalidate it before +** the database connection D is closed. +** ** The sqlite3_deserialize() interface will fail with SQLITE_BUSY if the ** database is currently in a read transaction or is involved in a backup ** operation. @@ -10605,6 +10727,13 @@ SQLITE_API unsigned char *sqlite3_serialize( ** S argument to sqlite3_deserialize(D,S,P,N,M,F) is "temp" then the ** function returns SQLITE_ERROR. ** +** The deserialized database should not be in [WAL mode]. If the database +** is in WAL mode, then any attempt to use the database file will result +** in an [SQLITE_CANTOPEN] error. The application can set the +** [file format version numbers] (bytes 18 and 19) of the input database P +** to 0x01 prior to invoking sqlite3_deserialize(D,S,P,N,M,F) to force the +** database file into rollback mode and work around this limitation. +** ** If sqlite3_deserialize(D,S,P,N,M,F) fails for any reason and if the ** SQLITE_DESERIALIZE_FREEONCLOSE bit is set in argument F, then ** [sqlite3_free()] is invoked on argument P prior to returning. @@ -11677,6 +11806,18 @@ SQLITE_API int sqlite3changeset_concat( ); +/* +** CAPI3REF: Upgrade the Schema of a Changeset/Patchset +*/ +SQLITE_API int sqlite3changeset_upgrade( + sqlite3 *db, + const char *zDb, + int nIn, const void *pIn, /* Input changeset */ + int *pnOut, void **ppOut /* OUT: Inverse of input */ +); + + + /* ** CAPI3REF: Changegroup Handle ** @@ -11723,6 +11864,38 @@ typedef struct sqlite3_changegroup sqlite3_changegroup; */ SQLITE_API int sqlite3changegroup_new(sqlite3_changegroup **pp); +/* +** CAPI3REF: Add a Schema to a Changegroup +** METHOD: sqlite3_changegroup_schema +** +** This method may be used to optionally enforce the rule that the changesets +** added to the changegroup handle must match the schema of database zDb +** ("main", "temp", or the name of an attached database). If +** sqlite3changegroup_add() is called to add a changeset that is not compatible +** with the configured schema, SQLITE_SCHEMA is returned and the changegroup +** object is left in an undefined state. +** +** A changeset schema is considered compatible with the database schema in +** the same way as for sqlite3changeset_apply(). Specifically, for each +** table in the changeset, there exists a database table with: +** +**
      +**
    • The name identified by the changeset, and +**
    • at least as many columns as recorded in the changeset, and +**
    • the primary key columns in the same position as recorded in +** the changeset. +**
    +** +** The output of the changegroup object always has the same schema as the +** database nominated using this function. In cases where changesets passed +** to sqlite3changegroup_add() have fewer columns than the corresponding table +** in the database schema, these are filled in using the default column +** values from the database schema. This makes it possible to combined +** changesets that have different numbers of columns for a single table +** within a changegroup, provided that they are otherwise compatible. +*/ +SQLITE_API int sqlite3changegroup_schema(sqlite3_changegroup*, sqlite3*, const char *zDb); + /* ** CAPI3REF: Add A Changeset To A Changegroup ** METHOD: sqlite3_changegroup @@ -11791,13 +11964,18 @@ SQLITE_API int sqlite3changegroup_new(sqlite3_changegroup **pp); ** If the new changeset contains changes to a table that is already present ** in the changegroup, then the number of columns and the position of the ** primary key columns for the table must be consistent. If this is not the -** case, this function fails with SQLITE_SCHEMA. If the input changeset -** appears to be corrupt and the corruption is detected, SQLITE_CORRUPT is -** returned. Or, if an out-of-memory condition occurs during processing, this -** function returns SQLITE_NOMEM. In all cases, if an error occurs the state -** of the final contents of the changegroup is undefined. +** case, this function fails with SQLITE_SCHEMA. Except, if the changegroup +** object has been configured with a database schema using the +** sqlite3changegroup_schema() API, then it is possible to combine changesets +** with different numbers of columns for a single table, provided that +** they are otherwise compatible. ** -** If no error occurs, SQLITE_OK is returned. +** If the input changeset appears to be corrupt and the corruption is +** detected, SQLITE_CORRUPT is returned. Or, if an out-of-memory condition +** occurs during processing, this function returns SQLITE_NOMEM. +** +** In all cases, if an error occurs the state of the final contents of the +** changegroup is undefined. If no error occurs, SQLITE_OK is returned. */ SQLITE_API int sqlite3changegroup_add(sqlite3_changegroup*, int nData, void *pData); @@ -12062,10 +12240,17 @@ SQLITE_API int sqlite3changeset_apply_v2( **
  2. an insert change if all fields of the conflicting row match ** the row being inserted. ** +** +**
    SQLITE_CHANGESETAPPLY_FKNOACTION
    +** If this flag it set, then all foreign key constraints in the target +** database behave as if they were declared with "ON UPDATE NO ACTION ON +** DELETE NO ACTION", even if they are actually CASCADE, RESTRICT, SET NULL +** or SET DEFAULT. */ #define SQLITE_CHANGESETAPPLY_NOSAVEPOINT 0x0001 #define SQLITE_CHANGESETAPPLY_INVERT 0x0002 #define SQLITE_CHANGESETAPPLY_IGNORENOOP 0x0004 +#define SQLITE_CHANGESETAPPLY_FKNOACTION 0x0008 /* ** CAPI3REF: Constants Passed To The Conflict Handler @@ -12631,8 +12816,11 @@ struct Fts5PhraseIter { ** created with the "columnsize=0" option. ** ** xColumnText: -** This function attempts to retrieve the text of column iCol of the -** current document. If successful, (*pz) is set to point to a buffer +** If parameter iCol is less than zero, or greater than or equal to the +** number of columns in the table, SQLITE_RANGE is returned. +** +** Otherwise, this function attempts to retrieve the text of column iCol of +** the current document. If successful, (*pz) is set to point to a buffer ** containing the text in utf-8 encoding, (*pn) is set to the size in bytes ** (not characters) of the buffer and SQLITE_OK is returned. Otherwise, ** if an error occurs, an SQLite error code is returned and the final values @@ -12642,8 +12830,10 @@ struct Fts5PhraseIter { ** Returns the number of phrases in the current query expression. ** ** xPhraseSize: -** Returns the number of tokens in phrase iPhrase of the query. Phrases -** are numbered starting from zero. +** If parameter iCol is less than zero, or greater than or equal to the +** number of phrases in the current query, as returned by xPhraseCount, +** 0 is returned. Otherwise, this function returns the number of tokens in +** phrase iPhrase of the query. Phrases are numbered starting from zero. ** ** xInstCount: ** Set *pnInst to the total number of occurrences of all phrases within @@ -12659,12 +12849,13 @@ struct Fts5PhraseIter { ** Query for the details of phrase match iIdx within the current row. ** Phrase matches are numbered starting from zero, so the iIdx argument ** should be greater than or equal to zero and smaller than the value -** output by xInstCount(). +** output by xInstCount(). If iIdx is less than zero or greater than +** or equal to the value returned by xInstCount(), SQLITE_RANGE is returned. ** -** Usually, output parameter *piPhrase is set to the phrase number, *piCol +** Otherwise, output parameter *piPhrase is set to the phrase number, *piCol ** to the column in which it occurs and *piOff the token offset of the -** first token of the phrase. Returns SQLITE_OK if successful, or an error -** code (i.e. SQLITE_NOMEM) if an error occurs. +** first token of the phrase. SQLITE_OK is returned if successful, or an +** error code (i.e. SQLITE_NOMEM) if an error occurs. ** ** This API can be quite slow if used with an FTS5 table created with the ** "detail=none" or "detail=column" option. @@ -12690,6 +12881,10 @@ struct Fts5PhraseIter { ** Invoking Api.xUserData() returns a copy of the pointer passed as ** the third argument to pUserData. ** +** If parameter iPhrase is less than zero, or greater than or equal to +** the number of phrases in the query, as returned by xPhraseCount(), +** this function returns SQLITE_RANGE. +** ** If the callback function returns any value other than SQLITE_OK, the ** query is abandoned and the xQueryPhrase function returns immediately. ** If the returned value is SQLITE_DONE, xQueryPhrase returns SQLITE_OK. @@ -12804,9 +12999,42 @@ struct Fts5PhraseIter { ** ** xPhraseNextColumn() ** See xPhraseFirstColumn above. +** +** xQueryToken(pFts5, iPhrase, iToken, ppToken, pnToken) +** This is used to access token iToken of phrase iPhrase of the current +** query. Before returning, output parameter *ppToken is set to point +** to a buffer containing the requested token, and *pnToken to the +** size of this buffer in bytes. +** +** If iPhrase or iToken are less than zero, or if iPhrase is greater than +** or equal to the number of phrases in the query as reported by +** xPhraseCount(), or if iToken is equal to or greater than the number of +** tokens in the phrase, SQLITE_RANGE is returned and *ppToken and *pnToken + are both zeroed. +** +** The output text is not a copy of the query text that specified the +** token. It is the output of the tokenizer module. For tokendata=1 +** tables, this includes any embedded 0x00 and trailing data. +** +** xInstToken(pFts5, iIdx, iToken, ppToken, pnToken) +** This is used to access token iToken of phrase hit iIdx within the +** current row. If iIdx is less than zero or greater than or equal to the +** value returned by xInstCount(), SQLITE_RANGE is returned. Otherwise, +** output variable (*ppToken) is set to point to a buffer containing the +** matching document token, and (*pnToken) to the size of that buffer in +** bytes. This API is not available if the specified token matches a +** prefix query term. In that case both output variables are always set +** to 0. +** +** The output text is not a copy of the document text that was tokenized. +** It is the output of the tokenizer module. For tokendata=1 tables, this +** includes any embedded 0x00 and trailing data. +** +** This API can be quite slow if used with an FTS5 table created with the +** "detail=none" or "detail=column" option. */ struct Fts5ExtensionApi { - int iVersion; /* Currently always set to 2 */ + int iVersion; /* Currently always set to 3 */ void *(*xUserData)(Fts5Context*); @@ -12841,6 +13069,13 @@ struct Fts5ExtensionApi { int (*xPhraseFirstColumn)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*); void (*xPhraseNextColumn)(Fts5Context*, Fts5PhraseIter*, int *piCol); + + /* Below this point are iVersion>=3 only */ + int (*xQueryToken)(Fts5Context*, + int iPhrase, int iToken, + const char **ppToken, int *pnToken + ); + int (*xInstToken)(Fts5Context*, int iIdx, int iToken, const char**, int*); }; /* diff --git a/src/libs/3rdparty/sqlite/sqlite3ext.h b/src/libs/3rdparty/sqlite/sqlite3ext.h index 71163809926..ae0949baf75 100644 --- a/src/libs/3rdparty/sqlite/sqlite3ext.h +++ b/src/libs/3rdparty/sqlite/sqlite3ext.h @@ -363,6 +363,9 @@ struct sqlite3_api_routines { int (*is_interrupted)(sqlite3*); /* Version 3.43.0 and later */ int (*stmt_explain)(sqlite3_stmt*,int); + /* Version 3.44.0 and later */ + void *(*get_clientdata)(sqlite3*,const char*); + int (*set_clientdata)(sqlite3*, const char*, void*, void(*)(void*)); }; /* @@ -693,6 +696,9 @@ typedef int (*sqlite3_loadext_entry)( #define sqlite3_is_interrupted sqlite3_api->is_interrupted /* Version 3.43.0 and later */ #define sqlite3_stmt_explain sqlite3_api->stmt_explain +/* Version 3.44.0 and later */ +#define sqlite3_get_clientdata sqlite3_api->get_clientdata +#define sqlite3_set_clientdata sqlite3_api->set_clientdata #endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */ #if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) From 2ba6b48df44cd5f68ae534c20451b0c1d6bf4d12 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Fri, 23 Feb 2024 12:54:16 +0100 Subject: [PATCH 108/176] QmlDesigner: Move thread local variables inside function Maybe make them inline but I think that is not working nice with DLLs. Change-Id: Ice220873bf4f9c87cb64cb3271aa1ca5cfb1dda2 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Thomas Hartmann Reviewed-by: --- .../projectstorage/projectstorage.cpp | 9 +++----- .../tracing/qmldesignertracing.cpp | 22 ++++++++++++------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp index 700a85bc112..3e493e87724 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp @@ -9,14 +9,11 @@ namespace QmlDesigner { -namespace { - -thread_local NanotraceHR::StringViewCategory projectStorageCategory_{ - "project storage"_t, Tracing::eventQueue(), projectStorageCategory}; -} - NanotraceHR::StringViewCategory &projectStorageCategory() { + thread_local NanotraceHR::StringViewCategory + projectStorageCategory_{"project storage"_t, Tracing::eventQueue(), projectStorageCategory}; + return projectStorageCategory_; } diff --git a/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.cpp b/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.cpp index 790be02b701..d49c6156a65 100644 --- a/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.cpp +++ b/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.cpp @@ -7,31 +7,37 @@ namespace QmlDesigner { namespace Tracing { namespace { + using TraceFile = NanotraceHR::TraceFile; -TraceFile traceFile{"qml_designer.json"}; - -thread_local NanotraceHR::EventQueueData - stringViewEventQueueData(traceFile); - -thread_local NanotraceHR::EventQueueData - stringViewWithStringArgumentsEventQueueData(traceFile); +TraceFile &traceFile() +{ + static TraceFile traceFile{"qml_designer.json"}; + return traceFile; +} } // namespace EventQueue &eventQueue() { + thread_local NanotraceHR::EventQueueData + stringViewEventQueueData(traceFile()); + return stringViewEventQueueData; } EventQueueWithStringArguments &eventQueueWithStringArguments() { + thread_local NanotraceHR:: + EventQueueData + stringViewWithStringArgumentsEventQueueData(traceFile()); + return stringViewWithStringArgumentsEventQueueData; } StringEventQueue &stringEventQueue() { thread_local NanotraceHR::EventQueueData eventQueue( - traceFile); + traceFile()); return eventQueue; } From 1110a7a4575fbd2b0c412dff540a12229f3a4088 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 28 Feb 2024 16:01:03 +0100 Subject: [PATCH 109/176] Nanotrace: Silence warning about exports The behavior between windows and unix is different, so we need a more complex approach. Change-Id: I614c480c1184c5d4be807496fa6ac13fd98506d8 Reviewed-by: Tim Jenssen --- src/libs/nanotrace/nanotraceglobals.h | 18 ++++++++++++++++-- src/libs/nanotrace/nanotracehr.cpp | 7 ++++--- src/libs/nanotrace/nanotracehr.h | 7 ++++--- 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/src/libs/nanotrace/nanotraceglobals.h b/src/libs/nanotrace/nanotraceglobals.h index 649408d69c9..40a55131bc2 100644 --- a/src/libs/nanotrace/nanotraceglobals.h +++ b/src/libs/nanotrace/nanotraceglobals.h @@ -7,10 +7,24 @@ #if defined(NANOTRACE_LIBRARY) #define NANOTRACE_EXPORT Q_DECL_EXPORT +# ifdef Q_OS_WINDOWS +# define NANOTRACE_EXPORT_EXTERN_TEMPLATE +# else +# define NANOTRACE_EXPORT_EXTERN_TEMPLATE Q_DECL_EXPORT +# endif +# ifdef Q_OS_WINDOWS +# define NANOTRACE_EXPORT_TEMPLATE Q_DECL_EXPORT +# else +# define NANOTRACE_EXPORT_TEMPLATE +# endif #elif defined(NANOTRACE_STATIC_LIBRARY) -#define NANOTRACE_EXPORT +# define NANOTRACE_EXPORT +# define NANOTRACE_EXPORT_EXTERN_TEMPLATE +# define NANOTRACE_EXPORT_TEMPLATE #else -#define NANOTRACE_EXPORT Q_DECL_IMPORT +# define NANOTRACE_EXPORT Q_DECL_IMPORT +# define NANOTRACE_EXPORT_EXTERN_TEMPLATE +# define NANOTRACE_EXPORT_TEMPLATE #endif #define NANOTRACESHARED_EXPORT NANOTRACE_EXPORT diff --git a/src/libs/nanotrace/nanotracehr.cpp b/src/libs/nanotrace/nanotracehr.cpp index dcf6eb8039a..b2295202209 100644 --- a/src/libs/nanotrace/nanotracehr.cpp +++ b/src/libs/nanotrace/nanotracehr.cpp @@ -263,8 +263,9 @@ void EventQueue::flush() } } -template class EventQueue; -template class EventQueue; -template class EventQueue; +template class NANOTRACE_EXPORT_TEMPLATE EventQueue; +template class NANOTRACE_EXPORT_TEMPLATE EventQueue; +template class NANOTRACE_EXPORT_TEMPLATE + EventQueue; } // namespace NanotraceHR diff --git a/src/libs/nanotrace/nanotracehr.h b/src/libs/nanotrace/nanotracehr.h index 51a492c3dbe..1e1a8dd23f1 100644 --- a/src/libs/nanotrace/nanotracehr.h +++ b/src/libs/nanotrace/nanotracehr.h @@ -500,9 +500,10 @@ using StringViewWithStringArgumentsEventQueue = EventQueue using StringEventQueue = EventQueue; -extern template class NANOTRACE_EXPORT EventQueue; -extern template class NANOTRACE_EXPORT EventQueue; -extern template class NANOTRACE_EXPORT EventQueue; +extern template class NANOTRACE_EXPORT_EXTERN_TEMPLATE EventQueue; +extern template class NANOTRACE_EXPORT_EXTERN_TEMPLATE EventQueue; +extern template class NANOTRACE_EXPORT_EXTERN_TEMPLATE + EventQueue; template class EventQueueData : public EventQueue From 689b1066b8caa6067da9ff1b3b21c59ae234fba9 Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Wed, 28 Feb 2024 18:30:24 +0100 Subject: [PATCH 110/176] QmlDesigner: suppress unknown attribute warnings Somehow MSVC does not follow cppreference: https://en.cppreference.com/w/cpp/language/attributes "All attributes unknown to an implementation are ignored without causing an error.(since C++17)" Change-Id: Ic67fc7b4297f981ce52f457a09fd919949050449 Reviewed-by: Marco Bubke --- src/libs/nanotrace/CMakeLists.txt | 2 ++ src/plugins/qmldesigner/CMakeLists.txt | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libs/nanotrace/CMakeLists.txt b/src/libs/nanotrace/CMakeLists.txt index f2fa830e2d3..baad068ca86 100644 --- a/src/libs/nanotrace/CMakeLists.txt +++ b/src/libs/nanotrace/CMakeLists.txt @@ -10,6 +10,8 @@ add_qtc_library(Nanotrace VISIBILITY_INLINES_HIDDEN OFF ) +target_compile_options(Nanotrace PUBLIC $<$:/wd5030>) + option(DESIGNSTUDIO_USE_NANOTRACE "Enables collecting performance data with nanotrace for Design Studio" OFF) extend_qtc_library(Nanotrace diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 69ce538a1f0..aa4750f81d1 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -9,7 +9,6 @@ if (APPLE) endif() add_compile_options("$<$:-Wno-error=maybe-uninitialized>") -add_compile_options("$<$:/wd5030>") env_with_default("QDS_USE_PROJECTSTORAGE" ENV_QDS_USE_PROJECTSTORAGE OFF) option(USE_PROJECTSTORAGE "Use ProjectStorage" ${ENV_QDS_USE_PROJECTSTORAGE}) From 85be58e31b8e051a6713e849e7d960e904c11786 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Mon, 26 Feb 2024 17:29:56 +0100 Subject: [PATCH 111/176] QmlDesigner: Modernize ownership in connection editor Because the member are forward declared we use an nested class to reduce the pointer and declare them there as value member. Change-Id: I0e7058333983c4d742d1d831990e9e63cd651562 Reviewed-by: Thomas Hartmann Reviewed-by: Reviewed-by: Qt CI Patch Build Bot --- .../connectioneditor/backendmodel.cpp | 5 +- .../connectioneditor/backendmodel.h | 2 +- .../connectioneditor/bindingmodel.cpp | 19 +- .../connectioneditor/bindingmodel.h | 71 +++---- .../connectioneditor/connectionmodel.cpp | 46 +++-- .../connectioneditor/connectionmodel.h | 187 +++++++++--------- .../connectioneditor/connectionview.cpp | 140 +++++++------ .../connectioneditor/connectionview.h | 13 +- .../dynamicpropertiesmodel.cpp | 14 +- .../connectioneditor/dynamicpropertiesmodel.h | 9 +- .../connectioneditor/propertytreemodel.cpp | 7 +- .../connectioneditor/propertytreemodel.h | 4 +- .../studio/studioquickwidget.h | 4 +- 13 files changed, 270 insertions(+), 251 deletions(-) diff --git a/src/plugins/qmldesigner/components/connectioneditor/backendmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/backendmodel.cpp index a5e41e8f234..5d9e9fe99c6 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/backendmodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/backendmodel.cpp @@ -19,9 +19,8 @@ namespace QmlDesigner { -BackendModel::BackendModel(ConnectionView *parent) : - QStandardItemModel(parent) - ,m_connectionView(parent) +BackendModel::BackendModel(ConnectionView *view) + : m_connectionView(view) { connect(this, &QStandardItemModel::dataChanged, this, &BackendModel::handleDataChanged); } diff --git a/src/plugins/qmldesigner/components/connectioneditor/backendmodel.h b/src/plugins/qmldesigner/components/connectioneditor/backendmodel.h index c4f148aa2e7..9feaff91943 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/backendmodel.h +++ b/src/plugins/qmldesigner/components/connectioneditor/backendmodel.h @@ -21,7 +21,7 @@ public: IsLocalColumn = 3, }; - BackendModel(ConnectionView *parent); + BackendModel(ConnectionView *view); ConnectionView *connectionView() const; diff --git a/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp index e3c47d996ca..f871bae84b4 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp @@ -20,10 +20,8 @@ namespace QmlDesigner { -BindingModel::BindingModel(ConnectionView *parent) - : QStandardItemModel(parent) - , m_connectionView(parent) - , m_delegate(new BindingModelBackendDelegate(this)) +BindingModel::BindingModel(ConnectionView *view) + : m_connectionView(view) { setHorizontalHeaderLabels(BindingModelItem::headerLabels()); } @@ -33,9 +31,9 @@ ConnectionView *BindingModel::connectionView() const return m_connectionView; } -BindingModelBackendDelegate *BindingModel::delegate() const +BindingModelBackendDelegate *BindingModel::delegate() { - return m_delegate; + return &m_delegate; } int BindingModel::currentIndex() const @@ -133,7 +131,7 @@ void BindingModel::setCurrentIndex(int i) m_currentIndex = i; emit currentIndexChanged(); } - m_delegate->update(currentProperty(), m_connectionView); + m_delegate.update(currentProperty(), m_connectionView); } void BindingModel::setCurrentProperty(const AbstractProperty &property) @@ -153,7 +151,7 @@ void BindingModel::updateItem(const BindingProperty &property) setCurrentProperty(property); } } - m_delegate->update(currentProperty(), m_connectionView); + m_delegate.update(currentProperty(), m_connectionView); } void BindingModel::removeItem(const AbstractProperty &property) @@ -248,9 +246,8 @@ void BindingModel::addModelNode(const ModelNode &node) appendRow(new BindingModelItem(property)); } -BindingModelBackendDelegate::BindingModelBackendDelegate(BindingModel *parent) - : QObject(parent) - , m_targetNode() +BindingModelBackendDelegate::BindingModelBackendDelegate() + : m_targetNode() , m_property() , m_sourceNode() , m_sourceNodeProperty() diff --git a/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.h b/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.h index 4f5032afc24..b57cc5c9587 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.h +++ b/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.h @@ -17,6 +17,39 @@ class BindingModelBackendDelegate; class BindingModelItem; class ConnectionView; +class BindingModelBackendDelegate : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QString targetNode READ targetNode NOTIFY targetNodeChanged) + Q_PROPERTY(StudioQmlComboBoxBackend *property READ property CONSTANT) + Q_PROPERTY(StudioQmlComboBoxBackend *sourceNode READ sourceNode CONSTANT) + Q_PROPERTY(StudioQmlComboBoxBackend *sourceProperty READ sourceProperty CONSTANT) + +signals: + void targetNodeChanged(); + +public: + BindingModelBackendDelegate(); + + void update(const BindingProperty &property, AbstractView *view); + +private: + QString targetNode() const; + void sourceNodeChanged(); + void sourcePropertyNameChanged() const; + void targetPropertyNameChanged() const; + + StudioQmlComboBoxBackend *property(); + StudioQmlComboBoxBackend *sourceNode(); + StudioQmlComboBoxBackend *sourceProperty(); + + QString m_targetNode; + StudioQmlComboBoxBackend m_property; + StudioQmlComboBoxBackend m_sourceNode; + StudioQmlComboBoxBackend m_sourceNodeProperty; +}; + class BindingModel : public QStandardItemModel { Q_OBJECT @@ -29,10 +62,10 @@ public: Q_PROPERTY(BindingModelBackendDelegate *delegate READ delegate CONSTANT) public: - BindingModel(ConnectionView *parent = nullptr); + BindingModel(ConnectionView *view); ConnectionView *connectionView() const; - BindingModelBackendDelegate *delegate() const; + BindingModelBackendDelegate *delegate(); int currentIndex() const; BindingProperty currentProperty() const; @@ -63,41 +96,9 @@ private: private: ConnectionView *m_connectionView = nullptr; - BindingModelBackendDelegate *m_delegate = nullptr; + BindingModelBackendDelegate m_delegate; int m_currentIndex = -1; }; -class BindingModelBackendDelegate : public QObject -{ - Q_OBJECT - - Q_PROPERTY(QString targetNode READ targetNode NOTIFY targetNodeChanged) - Q_PROPERTY(StudioQmlComboBoxBackend *property READ property CONSTANT) - Q_PROPERTY(StudioQmlComboBoxBackend *sourceNode READ sourceNode CONSTANT) - Q_PROPERTY(StudioQmlComboBoxBackend *sourceProperty READ sourceProperty CONSTANT) - -signals: - void targetNodeChanged(); - -public: - BindingModelBackendDelegate(BindingModel *parent = nullptr); - - void update(const BindingProperty &property, AbstractView *view); - -private: - QString targetNode() const; - void sourceNodeChanged(); - void sourcePropertyNameChanged() const; - void targetPropertyNameChanged() const; - - StudioQmlComboBoxBackend *property(); - StudioQmlComboBoxBackend *sourceNode(); - StudioQmlComboBoxBackend *sourceProperty(); - - QString m_targetNode; - StudioQmlComboBoxBackend m_property; - StudioQmlComboBoxBackend m_sourceNode; - StudioQmlComboBoxBackend m_sourceNodeProperty; -}; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp index 0741b43430f..d0d6d8f060c 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp @@ -55,9 +55,9 @@ bool isConnection(const QmlDesigner::ModelNode &modelNode) namespace QmlDesigner { -ConnectionModel::ConnectionModel(ConnectionView *parent) - : QStandardItemModel(parent), m_connectionView(parent), - m_delegate(new ConnectionModelBackendDelegate(this)) +ConnectionModel::ConnectionModel(ConnectionView *view) + : m_connectionView(view) + , m_delegate{this} { connect(this, &QStandardItemModel::dataChanged, this, &ConnectionModel::handleDataChanged); } @@ -90,7 +90,7 @@ void ConnectionModel::resetModel() addModelNode(modelNode); } endResetModel(); - m_delegate->update(); + m_delegate.update(); } SignalHandlerProperty ConnectionModel::signalHandlerPropertyForRow(int rowNumber) const @@ -480,7 +480,7 @@ void ConnectionModel::setCurrentIndex(int i) m_currentIndex = i; emit currentIndexChanged(); } - m_delegate->setCurrentRow(i); + m_delegate.setCurrentRow(i); } int ConnectionModel::currentIndex() const @@ -506,7 +506,7 @@ void ConnectionModel::nodeAboutToBeRemoved(const ModelNode &removedNode) if (selectedSignal.isValid()) { ModelNode targetNode = getTargetNodeForConnection(selectedSignal.parentModelNode()); if (targetNode == removedNode) { - emit m_delegate->popupShouldClose(); + emit m_delegate.popupShouldClose(); } } } @@ -517,9 +517,9 @@ void ConnectionModel::handleException() resetModel(); } -ConnectionModelBackendDelegate *ConnectionModel::delegate() const +ConnectionModelBackendDelegate *ConnectionModel::delegate() { - return m_delegate; + return &m_delegate; } void ConnectionModel::handleDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) @@ -676,10 +676,13 @@ QHash ConnectionModel::roleNames() const return roleNames; } -ConnectionModelBackendDelegate::ConnectionModelBackendDelegate(ConnectionModel *parent) - : QObject(parent), m_signalDelegate(parent->connectionView()), m_okStatementDelegate(parent), - m_koStatementDelegate(parent), m_conditionListModel(parent), - m_propertyTreeModel(parent->connectionView()), m_propertyListProxyModel(&m_propertyTreeModel) +ConnectionModelBackendDelegate::ConnectionModelBackendDelegate(ConnectionModel *model) + : m_signalDelegate(model->connectionView()) + , m_okStatementDelegate(model) + , m_koStatementDelegate(model) + , m_conditionListModel(model) + , m_propertyTreeModel(model->connectionView()) + , m_propertyListProxyModel(&m_propertyTreeModel) { connect(&m_signalDelegate, &PropertyTreeModelDelegate::commitData, this, [this] { @@ -1214,10 +1217,12 @@ void ConnectionModelBackendDelegate::commitNewSource(const QString &source) static ConnectionEditorStatements::MatchedStatement emptyStatement; -ConnectionModelStatementDelegate::ConnectionModelStatementDelegate(ConnectionModel *parent) - : QObject(parent), m_functionDelegate(parent->connectionView()), - m_lhsDelegate(parent->connectionView()), m_rhsAssignmentDelegate(parent->connectionView()), - m_statement(emptyStatement), m_model(parent) +ConnectionModelStatementDelegate::ConnectionModelStatementDelegate(ConnectionModel *model) + : m_functionDelegate(model->connectionView()) + , m_lhsDelegate(model->connectionView()) + , m_rhsAssignmentDelegate(model->connectionView()) + , m_statement(emptyStatement) + , m_model(model) { m_functionDelegate.setPropertyType(PropertyTreeModel::SlotType); @@ -1657,8 +1662,9 @@ QString ConnectionModelStatementDelegate::baseStateName() const static ConnectionEditorStatements::MatchedCondition emptyCondition; -ConditionListModel::ConditionListModel(ConnectionModel *parent) - : m_connectionModel(parent), m_condition(emptyCondition) +ConditionListModel::ConditionListModel(ConnectionModel *model) + : m_connectionModel(model) + , m_condition(emptyCondition) {} int ConditionListModel::rowCount(const QModelIndex & /*parent*/) const @@ -2139,12 +2145,12 @@ ConnectionEditorStatements::ComparativeStatement ConditionListModel::toStatement void QmlDesigner::ConnectionModel::modelAboutToBeDetached() { - emit m_delegate->popupShouldClose(); + emit m_delegate.popupShouldClose(); } void ConnectionModel::showPopup() { - emit m_delegate->popupShouldOpen(); + emit m_delegate.popupShouldOpen(); } } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h index d45014914d4..161b677c004 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h @@ -10,6 +10,8 @@ #include #include +#include + namespace QmlDesigner { class AbstractProperty; @@ -19,92 +21,7 @@ class SignalHandlerProperty; class VariantProperty; class ConnectionView; -class ConnectionModelBackendDelegate; - -class ConnectionModel : public QStandardItemModel -{ - Q_OBJECT - - Q_PROPERTY(ConnectionModelBackendDelegate *delegate READ delegate CONSTANT) - -public: - Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged) - -public: - enum ColumnRoles { - TargetModelNodeRow = 0, - TargetPropertyNameRow = 1, - SourceRow = 2 - }; - enum UserRoles { - InternalIdRole = Qt::UserRole + 1, - TargetPropertyNameRole, - TargetNameRole, - ActionTypeRole - }; - ConnectionModel(ConnectionView *parent = nullptr); - - Qt::ItemFlags flags(const QModelIndex &modelIndex) const override; - - void resetModel(); - SignalHandlerProperty signalHandlerPropertyForRow(int rowNumber) const; - ConnectionView *connectionView() const; - - QStringList getSignalsForRow(int row) const; - QStringList getflowActionTriggerForRow(int row) const; - ModelNode getTargetNodeForConnection(const ModelNode &connection) const; - - void addConnection(const PropertyName &signalName = {}); - - void bindingPropertyChanged(const BindingProperty &bindingProperty); - void variantPropertyChanged(const VariantProperty &variantProperty); - void abstractPropertyChanged(const AbstractProperty &abstractProperty); - - void deleteConnectionByRow(int currentRow); - void removeRowFromTable(const SignalHandlerProperty &property); - - Q_INVOKABLE void add(); - Q_INVOKABLE void remove(int row); - - void setCurrentIndex(int i); - int currentIndex() const; - - void selectProperty(const SignalHandlerProperty &property); - - void nodeAboutToBeRemoved(const ModelNode &removedNode); - void modelAboutToBeDetached(); - - void showPopup(); - -signals: - void currentIndexChanged(); - -protected: - void addModelNode(const ModelNode &modelNode); - void addConnection(const ModelNode &modelNode); - void addSignalHandler(const SignalHandlerProperty &bindingProperty); - void removeModelNode(const ModelNode &modelNode); - void removeConnection(const ModelNode &modelNode); - void updateSource(int row); - void updateSignalName(int rowNumber); - void updateTargetNode(int rowNumber); - void updateCustomData(QStandardItem *item, const SignalHandlerProperty &signalHandlerProperty); - QStringList getPossibleSignalsForConnection(const ModelNode &connection) const; - - QHash roleNames() const override; - -private: - void handleDataChanged(const QModelIndex &topLeft, const QModelIndex& bottomRight); - void handleException(); - ConnectionModelBackendDelegate *delegate() const; - -private: - ConnectionView *m_connectionView; - bool m_lock = false; - QString m_exceptionError; - ConnectionModelBackendDelegate *m_delegate = nullptr; - int m_currentIndex = -1; -}; +class ConnectionModel; class ConditionListModel : public QAbstractListModel { @@ -125,7 +42,7 @@ public: QString value; }; - ConditionListModel(ConnectionModel *parent = nullptr); + ConditionListModel(ConnectionModel *model); int rowCount(const QModelIndex &parent = QModelIndex()) const override; @@ -136,8 +53,7 @@ public: void setCondition(ConnectionEditorStatements::MatchedCondition &condition); ConnectionEditorStatements::MatchedCondition &condition(); - static ConditionToken tokenFromConditionToken( - const ConnectionEditorStatements::ConditionToken &token); + static ConditionToken tokenFromConditionToken(const ConnectionEditorStatements::ConditionToken &token); static ConditionToken tokenFromComparativeStatement( const ConnectionEditorStatements::ComparativeStatement &token); @@ -196,7 +112,7 @@ class ConnectionModelStatementDelegate : public QObject Q_OBJECT public: - explicit ConnectionModelStatementDelegate(ConnectionModel *parent = nullptr); + explicit ConnectionModelStatementDelegate(ConnectionModel *model); enum ActionType { CallFunction, Assign, ChangeState, SetProperty, PrintMessage, Custom }; @@ -277,12 +193,11 @@ class ConnectionModelBackendDelegate : public QObject Q_PROPERTY(PropertyListProxyModel *propertyListProxyModel READ propertyListProxyModel CONSTANT) public: - explicit ConnectionModelBackendDelegate(ConnectionModel *parent = nullptr); + explicit ConnectionModelBackendDelegate(ConnectionModel *model); using ActionType = ConnectionModelStatementDelegate::ActionType; - Q_INVOKABLE void changeActionType( - QmlDesigner::ConnectionModelStatementDelegate::ActionType actionType); + Q_INVOKABLE void changeActionType(QmlDesigner::ConnectionModelStatementDelegate::ActionType actionType); Q_INVOKABLE void addCondition(); Q_INVOKABLE void removeCondition(); @@ -351,4 +266,90 @@ private: bool m_blockReflection = false; }; +class ConnectionModel : public QStandardItemModel +{ + Q_OBJECT + + Q_PROPERTY(ConnectionModelBackendDelegate *delegate READ delegate CONSTANT) + +public: + Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged) + +public: + enum ColumnRoles { + TargetModelNodeRow = 0, + TargetPropertyNameRow = 1, + SourceRow = 2 + }; + enum UserRoles { + InternalIdRole = Qt::UserRole + 1, + TargetPropertyNameRole, + TargetNameRole, + ActionTypeRole + }; + + ConnectionModel(ConnectionView *view); + + Qt::ItemFlags flags(const QModelIndex &modelIndex) const override; + + void resetModel(); + SignalHandlerProperty signalHandlerPropertyForRow(int rowNumber) const; + ConnectionView *connectionView() const; + + QStringList getSignalsForRow(int row) const; + QStringList getflowActionTriggerForRow(int row) const; + ModelNode getTargetNodeForConnection(const ModelNode &connection) const; + + void addConnection(const PropertyName &signalName = {}); + + void bindingPropertyChanged(const BindingProperty &bindingProperty); + void variantPropertyChanged(const VariantProperty &variantProperty); + void abstractPropertyChanged(const AbstractProperty &abstractProperty); + + void deleteConnectionByRow(int currentRow); + void removeRowFromTable(const SignalHandlerProperty &property); + + Q_INVOKABLE void add(); + Q_INVOKABLE void remove(int row); + + void setCurrentIndex(int i); + int currentIndex() const; + + void selectProperty(const SignalHandlerProperty &property); + + void nodeAboutToBeRemoved(const ModelNode &removedNode); + void modelAboutToBeDetached(); + + void showPopup(); + +signals: + void currentIndexChanged(); + +protected: + void addModelNode(const ModelNode &modelNode); + void addConnection(const ModelNode &modelNode); + void addSignalHandler(const SignalHandlerProperty &bindingProperty); + void removeModelNode(const ModelNode &modelNode); + void removeConnection(const ModelNode &modelNode); + void updateSource(int row); + void updateSignalName(int rowNumber); + void updateTargetNode(int rowNumber); + void updateCustomData(QStandardItem *item, const SignalHandlerProperty &signalHandlerProperty); + QStringList getPossibleSignalsForConnection(const ModelNode &connection) const; + + QHash roleNames() const override; + +private: + void handleDataChanged(const QModelIndex &topLeft, const QModelIndex& bottomRight); + void handleException(); + ConnectionModelBackendDelegate *delegate(); + +private: + ConnectionView *m_connectionView; + bool m_lock = false; + QString m_exceptionError; + ConnectionModelBackendDelegate m_delegate; + int m_currentIndex = -1; +}; + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp index ab010e3f355..786fd04f1b0 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp @@ -47,7 +47,10 @@ class ConnectionViewQuickWidget : public StudioQuickWidget // Q_OBJECT carefull public: - ConnectionViewQuickWidget(ConnectionView *connectionEditorView) + ConnectionViewQuickWidget(ConnectionView *connectionEditorView, + ConnectionModel *connectionModel, + BindingModel *bindingModel, + DynamicPropertiesModel *dynamicPropertiesModel) : m_connectionEditorView(connectionEditorView) { @@ -70,15 +73,11 @@ public: qmlRegisterAnonymousType( "ConnectionsEditorEditorBackend", 1); - map->setProperties( - {{"connectionModel", QVariant::fromValue(m_connectionEditorView->connectionModel())}}); + map->setProperties({{"connectionModel", QVariant::fromValue(connectionModel)}}); - map->setProperties( - {{"bindingModel", QVariant::fromValue(m_connectionEditorView->bindingModel())}}); + map->setProperties({{"bindingModel", QVariant::fromValue(bindingModel)}}); - map->setProperties( - {{"dynamicPropertiesModel", - QVariant::fromValue(m_connectionEditorView->dynamicPropertiesModel())}}); + map->setProperties({{"dynamicPropertiesModel", QVariant::fromValue(dynamicPropertiesModel)}}); qmlRegisterType("ConnectionsEditorEditorBackend", 1, @@ -141,75 +140,92 @@ private: QShortcut *m_qmlSourceUpdateShortcut; }; +struct ConnectionView::ConnectionViewData +{ + ConnectionViewData(ConnectionView *view) + : connectionModel{view} + , bindingModel{view} + , dynamicPropertiesModel{false, view} + , backendModel{view} + , propertyTreeModel{view} + , connectionViewQuickWidget{Utils::makeUniqueObjectPtr( + view, &connectionModel, &bindingModel, &dynamicPropertiesModel)} + {} + + ConnectionModel connectionModel; + BindingModel bindingModel; + DynamicPropertiesModel dynamicPropertiesModel; + BackendModel backendModel; + PropertyTreeModel propertyTreeModel; + int currentIndex = 0; + + // Ensure that QML is deleted first to avoid calling back to C++. + Utils::UniqueObjectPtr connectionViewQuickWidget; +}; + ConnectionView::ConnectionView(ExternalDependenciesInterface &externalDependencies) : AbstractView{externalDependencies} - , m_connectionModel(new ConnectionModel(this)) - , m_bindingModel(new BindingModel(this)) - , m_dynamicPropertiesModel(new DynamicPropertiesModel(false, this)) - , m_backendModel(new BackendModel(this)) - , m_connectionViewQuickWidget(new ConnectionViewQuickWidget(this)) + , d{std::make_unique(this)} {} ConnectionView::~ConnectionView() { - // Ensure that QML is deleted first to avoid calling back to C++. - delete m_connectionViewQuickWidget.data(); } void ConnectionView::modelAttached(Model *model) { AbstractView::modelAttached(model); - bindingModel()->reset(); - dynamicPropertiesModel()->reset(); - connectionModel()->resetModel(); - backendModel()->resetModel(); + d->bindingModel.reset(); + d->dynamicPropertiesModel.reset(); + d->connectionModel.resetModel(); + d->backendModel.resetModel(); } void ConnectionView::modelAboutToBeDetached(Model *model) { AbstractView::modelAboutToBeDetached(model); - bindingModel()->reset(); - dynamicPropertiesModel()->reset(); - connectionModel()->resetModel(); - connectionModel()->modelAboutToBeDetached(); + d->bindingModel.reset(); + d->dynamicPropertiesModel.reset(); + d->connectionModel.resetModel(); + d->connectionModel.modelAboutToBeDetached(); } void ConnectionView::nodeCreated(const ModelNode & /*createdNode*/) { - connectionModel()->resetModel(); + d->connectionModel.resetModel(); } void ConnectionView::nodeAboutToBeRemoved(const ModelNode &removedNode) { - connectionModel()->nodeAboutToBeRemoved(removedNode); + d->connectionModel.nodeAboutToBeRemoved(removedNode); } void ConnectionView::nodeRemoved(const ModelNode & /*removedNode*/, const NodeAbstractProperty & /*parentProperty*/, AbstractView::PropertyChangeFlags /*propertyChange*/) { - connectionModel()->resetModel(); + d->connectionModel.resetModel(); } void ConnectionView::nodeReparented(const ModelNode & /*node*/, const NodeAbstractProperty & /*newPropertyParent*/, const NodeAbstractProperty & /*oldPropertyParent*/, AbstractView::PropertyChangeFlags /*propertyChange*/) { - connectionModel()->resetModel(); + d->connectionModel.resetModel(); } void ConnectionView::nodeIdChanged(const ModelNode & /*node*/, const QString & /*newId*/, const QString & /*oldId*/) { - connectionModel()->resetModel(); - bindingModel()->reset(); - dynamicPropertiesModel()->reset(); + d->connectionModel.resetModel(); + d->bindingModel.reset(); + d->dynamicPropertiesModel.reset(); } void ConnectionView::propertiesRemoved(const QList &propertyList) { for (const AbstractProperty &property : propertyList) { if (property.isDefaultProperty()) - connectionModel()->resetModel(); + d->connectionModel.resetModel(); - dynamicPropertiesModel()->dispatchPropertyChanges(property); + d->dynamicPropertiesModel.dispatchPropertyChanges(property); } } @@ -217,12 +233,12 @@ void ConnectionView::propertiesAboutToBeRemoved(const QList &p { for (const AbstractProperty &property : propertyList) { if (property.isBindingProperty()) { - bindingModel()->removeItem(property); - dynamicPropertiesModel()->removeItem(property); + d->bindingModel.removeItem(property); + d->dynamicPropertiesModel.removeItem(property); } else if (property.isVariantProperty()) { - dynamicPropertiesModel()->removeItem(property); + d->dynamicPropertiesModel.removeItem(property); } else if (property.isSignalHandlerProperty()) { - connectionModel()->removeRowFromTable(property.toSignalHandlerProperty()); + d->connectionModel.removeRowFromTable(property.toSignalHandlerProperty()); } } } @@ -232,13 +248,13 @@ void ConnectionView::variantPropertiesChanged(const QList &prop { for (const VariantProperty &variantProperty : propertyList) { if (variantProperty.isDynamic()) - dynamicPropertiesModel()->updateItem(variantProperty); + d->dynamicPropertiesModel.updateItem(variantProperty); if (variantProperty.isDynamic() && variantProperty.parentModelNode().isRootNode()) - backendModel()->resetModel(); + d->backendModel.resetModel(); - connectionModel()->variantPropertyChanged(variantProperty); + d->connectionModel.variantPropertyChanged(variantProperty); - dynamicPropertiesModel()->dispatchPropertyChanges(variantProperty); + d->dynamicPropertiesModel.dispatchPropertyChanges(variantProperty); } } @@ -246,15 +262,15 @@ void ConnectionView::bindingPropertiesChanged(const QList &prop AbstractView::PropertyChangeFlags /*propertyChange*/) { for (const BindingProperty &bindingProperty : propertyList) { - bindingModel()->updateItem(bindingProperty); + d->bindingModel.updateItem(bindingProperty); if (bindingProperty.isDynamic()) - dynamicPropertiesModel()->updateItem(bindingProperty); + d->dynamicPropertiesModel.updateItem(bindingProperty); if (bindingProperty.isDynamic() && bindingProperty.parentModelNode().isRootNode()) - backendModel()->resetModel(); + d->backendModel.resetModel(); - connectionModel()->bindingPropertyChanged(bindingProperty); + d->connectionModel.bindingPropertyChanged(bindingProperty); - dynamicPropertiesModel()->dispatchPropertyChanges(bindingProperty); + d->dynamicPropertiesModel.dispatchPropertyChanges(bindingProperty); } } @@ -262,29 +278,29 @@ void ConnectionView::signalHandlerPropertiesChanged(const QVectorabstractPropertyChanged(signalHandlerProperty); + d->connectionModel.abstractPropertyChanged(signalHandlerProperty); } void ConnectionView::selectedNodesChanged(const QList & selectedNodeList, const QList & /*lastSelectedNodeList*/) { - bindingModel()->reset(selectedNodeList); - dynamicPropertiesModel()->reset(); + d->bindingModel.reset(selectedNodeList); + d->dynamicPropertiesModel.reset(); } void ConnectionView::importsChanged(const Imports & /*addedImports*/, const Imports & /*removedImports*/) { - backendModel()->resetModel(); + d->backendModel.resetModel(); } void ConnectionView::currentStateChanged(const ModelNode &) { - dynamicPropertiesModel()->reset(); + d->dynamicPropertiesModel.reset(); } WidgetInfo ConnectionView::widgetInfo() { - return createWidgetInfo(m_connectionViewQuickWidget.data(), + return createWidgetInfo(d->connectionViewQuickWidget.get(), QLatin1String("ConnectionView"), WidgetInfo::LeftPane, 0, @@ -303,35 +319,35 @@ bool ConnectionView::isWidgetEnabled() ConnectionModel *ConnectionView::connectionModel() const { - return m_connectionModel; + return &d->connectionModel; } BindingModel *ConnectionView::bindingModel() const { - return m_bindingModel; + return &d->bindingModel; } DynamicPropertiesModel *ConnectionView::dynamicPropertiesModel() const { - return m_dynamicPropertiesModel; + return &d->dynamicPropertiesModel; } BackendModel *ConnectionView::backendModel() const { - return m_backendModel; + return &d->backendModel; } int ConnectionView::currentIndex() const { - return m_currentIndex; + return d->currentIndex; } void ConnectionView::setCurrentIndex(int i) { - if (m_currentIndex == i) + if (d->currentIndex == i) return; - m_currentIndex = i; + d->currentIndex = i; emit currentIndexChanged(); } @@ -362,8 +378,8 @@ void ConnectionView::customNotification(const AbstractView *, QTC_ASSERT(data.count() == 1, return ); const PropertyName name = data.first().toString().toUtf8(); - m_connectionModel->addConnection(name); - m_connectionModel->showPopup(); + d->connectionModel.addConnection(name); + d->connectionModel.showPopup(); } else if (identifier == EditConnectionNotification) { QTC_ASSERT(nodeList.count() == 1, return ); ModelNode modelNode = nodeList.first(); @@ -374,8 +390,8 @@ void ConnectionView::customNotification(const AbstractView *, QTC_ASSERT(modelNode.hasSignalHandlerProperty(name), return ); - m_connectionModel->selectProperty(modelNode.signalHandlerProperty(name)); - m_connectionModel->showPopup(); + d->connectionModel.selectProperty(modelNode.signalHandlerProperty(name)); + d->connectionModel.showPopup(); } } diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionview.h b/src/plugins/qmldesigner/components/connectioneditor/connectionview.h index 12a53d43a9e..1cf3aa26dd1 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionview.h +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionview.h @@ -6,6 +6,8 @@ #include #include +#include + #include QT_BEGIN_NAMESPACE @@ -81,15 +83,10 @@ signals: void currentIndexChanged(); private: - ConnectionModel *m_connectionModel; - BindingModel *m_bindingModel; - DynamicPropertiesModel *m_dynamicPropertiesModel; - BackendModel *m_backendModel; - PropertyTreeModel *m_propertyTreeModel; - PropertyListProxyModel *m_propertyListProxyModel; - int m_currentIndex = 0; + struct ConnectionViewData; - QPointer m_connectionViewQuickWidget; +private: + std::unique_ptr d; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp index 5cf90ca6fd8..9fdd3daec31 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp @@ -23,10 +23,9 @@ namespace QmlDesigner { -DynamicPropertiesModel::DynamicPropertiesModel(bool exSelection, AbstractView *parent) - : QStandardItemModel(parent) - , m_view(parent) - , m_delegate(new DynamicPropertiesModelBackendDelegate(this)) +DynamicPropertiesModel::DynamicPropertiesModel(bool exSelection, AbstractView *view) + : m_view(view) + , m_delegate(std::make_unique()) , m_explicitSelection(exSelection) { setHorizontalHeaderLabels(DynamicPropertiesItem::headerLabels()); @@ -39,7 +38,7 @@ AbstractView *DynamicPropertiesModel::view() const DynamicPropertiesModelBackendDelegate *DynamicPropertiesModel::delegate() const { - return m_delegate; + return m_delegate.get(); } int DynamicPropertiesModel::currentIndex() const @@ -383,9 +382,8 @@ void DynamicPropertiesModel::setSelectedNode(const ModelNode &node) reset(); } -DynamicPropertiesModelBackendDelegate::DynamicPropertiesModelBackendDelegate(DynamicPropertiesModel *parent) - : QObject(parent) - , m_internalNodeId(std::nullopt) +DynamicPropertiesModelBackendDelegate::DynamicPropertiesModelBackendDelegate() + : m_internalNodeId(std::nullopt) { m_type.setModel({"int", "bool", "var", "real", "string", "url", "color"}); connect(&m_type, &StudioQmlComboBoxBackend::activated, this, [this] { handleTypeChanged(); }); diff --git a/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.h b/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.h index 8ae4abf6d59..c2beed87304 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.h +++ b/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.h @@ -7,8 +7,11 @@ #include "nodeinstanceglobal.h" #include "studioquickwidget.h" + #include +#include + namespace QmlDesigner { class AbstractView; @@ -28,7 +31,7 @@ public: Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged) Q_PROPERTY(DynamicPropertiesModelBackendDelegate *delegate READ delegate CONSTANT) - DynamicPropertiesModel(bool explicitSelection, AbstractView *parent); + DynamicPropertiesModel(bool explicitSelection, AbstractView *view); AbstractView *view() const; DynamicPropertiesModelBackendDelegate *delegate() const; @@ -76,7 +79,7 @@ public: private: AbstractView *m_view = nullptr; - DynamicPropertiesModelBackendDelegate *m_delegate = nullptr; + std::unique_ptr m_delegate; int m_currentIndex = -1; // TODO: Remove. @@ -94,7 +97,7 @@ class DynamicPropertiesModelBackendDelegate : public QObject Q_PROPERTY(StudioQmlTextBackend *value READ value CONSTANT) public: - DynamicPropertiesModelBackendDelegate(DynamicPropertiesModel *parent = nullptr); + DynamicPropertiesModelBackendDelegate(); void update(const AbstractProperty &property); diff --git a/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.cpp index 4189496316f..cefaf69506d 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.cpp @@ -163,8 +163,8 @@ std::vector properityLists() return result; } -PropertyTreeModel::PropertyTreeModel(ConnectionView *parent) - : QAbstractItemModel(parent), m_connectionView(parent) +PropertyTreeModel::PropertyTreeModel(ConnectionView *view) + : m_connectionView(view) {} void PropertyTreeModel::resetModel() @@ -883,7 +883,8 @@ QString PropertyListProxyModel::parentName() const return m_treeModel->data(m_parentIndex, PropertyTreeModel::UserRoles::PropertyNameRole).toString(); } -PropertyTreeModelDelegate::PropertyTreeModelDelegate(ConnectionView *parent) : m_model(parent) +PropertyTreeModelDelegate::PropertyTreeModelDelegate(ConnectionView *view) + : m_model(view) { connect(&m_nameCombboBox, &StudioQmlComboBoxBackend::activated, this, [this] { handleNameChanged(); diff --git a/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.h b/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.h index 07e1401c4af..39af4857654 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.h +++ b/src/plugins/qmldesigner/components/connectioneditor/propertytreemodel.h @@ -49,7 +49,7 @@ public: BoolType }; - PropertyTreeModel(ConnectionView *parent = nullptr); + PropertyTreeModel(ConnectionView *view); void resetModel(); @@ -187,7 +187,7 @@ class PropertyTreeModelDelegate : public QObject Q_PROPERTY(StudioQmlComboBoxBackend *id READ idCombboBox CONSTANT) public: - explicit PropertyTreeModelDelegate(ConnectionView *parent = nullptr); + explicit PropertyTreeModelDelegate(ConnectionView *view); void setPropertyType(PropertyTreeModel::PropertyTypes type); void setup(const QString &id, const QString &name, bool *nameExists = nullptr); void setupNameComboBox(const QString &id, const QString &name, bool *nameExists); diff --git a/src/plugins/qmldesignerbase/studio/studioquickwidget.h b/src/plugins/qmldesignerbase/studio/studioquickwidget.h index 5c4bad8dae3..a02cb2ed51f 100644 --- a/src/plugins/qmldesignerbase/studio/studioquickwidget.h +++ b/src/plugins/qmldesignerbase/studio/studioquickwidget.h @@ -55,7 +55,7 @@ class QMLDESIGNERBASE_EXPORT StudioQmlTextBackend : public QObject Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged) public: - explicit StudioQmlTextBackend(QObject *parent = nullptr) : QObject(parent) {} + StudioQmlTextBackend() = default; void setText(const QString &text) { @@ -95,7 +95,7 @@ class QMLDESIGNERBASE_EXPORT StudioQmlComboBoxBackend : public QObject Q_PROPERTY(QStringList model READ model NOTIFY modelChanged) //TODO turn into model public: - explicit StudioQmlComboBoxBackend(QObject *parent = nullptr) : QObject(parent) {} + StudioQmlComboBoxBackend() = default; void setModel(const QStringList &model) { From 942b5b62bb43f44ab9d6fe409434b057b23b8c6d Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Thu, 29 Feb 2024 11:49:29 +0200 Subject: [PATCH 112/176] EffectComposer: Silence warning Change-Id: Id2cd48e3976b5bd33c8951d706f0186c7f8a8d87 Reviewed-by: Mahmoud Badri Reviewed-by: Miikka Heikkinen Reviewed-by: Reviewed-by: Qt CI Patch Build Bot --- src/plugins/effectcomposer/effectcomposermodel.cpp | 4 ++-- src/plugins/effectcomposer/effectcomposermodel.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/plugins/effectcomposer/effectcomposermodel.cpp b/src/plugins/effectcomposer/effectcomposermodel.cpp index 1324493de8f..5c2b1fc3cd6 100644 --- a/src/plugins/effectcomposer/effectcomposermodel.cpp +++ b/src/plugins/effectcomposer/effectcomposermodel.cpp @@ -584,7 +584,7 @@ R"( return s; } -QString EffectComposerModel::getDesignerSpecifics(const QString &name) const +QString EffectComposerModel::getDesignerSpecifics() const { QString s; @@ -1014,7 +1014,7 @@ void EffectComposerModel::saveResources(const QString &name) // Create designer property sheet // Since this is in subdir, no need to add it to newFileNames - QString specContent = getDesignerSpecifics(name); + QString specContent = getDesignerSpecifics(); QString specFileName("%1SpecificsDynamic.qml"); specFileName = specFileName.arg(name); Utils::FilePath specPath = designerPath.resolvePath(specFileName); diff --git a/src/plugins/effectcomposer/effectcomposermodel.h b/src/plugins/effectcomposer/effectcomposermodel.h index f851fc3dddc..bd4040efc27 100644 --- a/src/plugins/effectcomposer/effectcomposermodel.h +++ b/src/plugins/effectcomposer/effectcomposermodel.h @@ -173,7 +173,7 @@ private: QString getQmlImagesString(bool localFiles); QString getQmlComponentString(bool localFiles); QString getGeneratedMessage() const; - QString getDesignerSpecifics(const QString &name) const; + QString getDesignerSpecifics() const; void connectCompositionNode(CompositionNode *node); From 4ef5338229d6f9ceba1c35a75c27c1669cc5bcdc Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Thu, 29 Feb 2024 13:36:22 +0100 Subject: [PATCH 113/176] qml2puppet: use branding from qtcreator repository To build a correct puppet with the correct version number do: cmake -DBUILD_DESIGNSTUDIO=ON no branding argument necessary anymore Change-Id: I74b25e3da296fe77d83892d0596f5da3c38ed6cf Reviewed-by: Mahmoud Badri --- src/tools/qml2puppet/CMakeLists.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/tools/qml2puppet/CMakeLists.txt b/src/tools/qml2puppet/CMakeLists.txt index 092c2473615..b2f5edf2c91 100644 --- a/src/tools/qml2puppet/CMakeLists.txt +++ b/src/tools/qml2puppet/CMakeLists.txt @@ -1,7 +1,11 @@ cmake_minimum_required(VERSION 3.16) +# standalone build if (NOT QT_CREATOR_API_DEFINED) - # standalone build + option(BUILD_DESIGNSTUDIO "Build puppet with Qt Design Studio version in the binary name." OFF) + if (BUILD_DESIGNSTUDIO) + list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../../dist/branding/qtdesignstudio") + endif() list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake") project(qml2puppet) From 409b9d904f790cb31dbc894a6ff1d20a41b5e305 Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Wed, 28 Feb 2024 16:35:39 +0100 Subject: [PATCH 114/176] QmlDesigner: refactor dynamiclicensecheck.h Change-Id: Ic806a32c87200219f6376356bf251cfd5927deed Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Patch Build Bot --- share/qtcreator/qmldesigner/toolbar/Main.qml | 2 +- .../components/toolbar/toolbarbackend.cpp | 2 +- src/plugins/qmldesigner/dynamiclicensecheck.h | 69 +++++++------------ 3 files changed, 28 insertions(+), 45 deletions(-) diff --git a/share/qtcreator/qmldesigner/toolbar/Main.qml b/share/qtcreator/qmldesigner/toolbar/Main.qml index 294422df3ed..7235028df68 100644 --- a/share/qtcreator/qmldesigner/toolbar/Main.qml +++ b/share/qtcreator/qmldesigner/toolbar/Main.qml @@ -336,7 +336,7 @@ Rectangle { anchors.rightMargin: 8 iconFont: StudioTheme.Constants.font buttonIcon: qsTr("Share") - visible: !root.flyoutEnabled + visible: !root.flyoutEnabled && backend.isSharingEnabled enabled: backend.isSharingEnabled tooltip: shareButton.enabled ? qsTr("Share your project online.") : qsTr("Sharing your project online is disabled in the Community Version.") diff --git a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp index b7ef9cbd58a..e9df928c96c 100644 --- a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp +++ b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp @@ -737,7 +737,7 @@ bool ToolBarBackend::projectOpened() const bool ToolBarBackend::isSharingEnabled() { - return !QmlDesigner::checkOpenSourceLicense(); + return QmlDesigner::checkEnterpriseLicense(); } void ToolBarBackend::launchGlobalAnnotations() diff --git a/src/plugins/qmldesigner/dynamiclicensecheck.h b/src/plugins/qmldesigner/dynamiclicensecheck.h index 95ffa8a7c44..35100c8fd91 100644 --- a/src/plugins/qmldesigner/dynamiclicensecheck.h +++ b/src/plugins/qmldesigner/dynamiclicensecheck.h @@ -54,38 +54,6 @@ inline bool dsLicenseCheckerPluginExists() } } // namespace Internal -inline FoundLicense checkLicense() -{ - static FoundLicense license = noLicense; - - if (license != noLicense) - return license; - - if (auto plugin = Internal::licenseCheckerPlugin()) { - bool retVal = false; - - bool success = QMetaObject::invokeMethod(plugin, - "evaluationLicense", - Qt::DirectConnection, - Q_RETURN_ARG(bool, retVal)); - - if (success && retVal) - return enterprise; - - retVal = false; - - success = QMetaObject::invokeMethod(plugin, - "qdsEnterpriseLicense", - Qt::DirectConnection, - Q_RETURN_ARG(bool, retVal)); - if (success && retVal) - return enterprise; - else - return professional; - } - return community; -} - inline QString licensee() { if (auto plugin = Internal::licenseCheckerPlugin()) { @@ -130,26 +98,41 @@ inline bool checkEnterpriseLicense() if (Internal::dsLicenseCheckerPluginExists()) return false; - return true; + return false; } -inline bool checkOpenSourceLicense() +inline FoundLicense checkLicense() { - if (auto plugin = Internal::dsLicenseCheckerPlugin()) { + static FoundLicense license = noLicense; + + if (license != noLicense) + return license; + + if (auto plugin = Internal::licenseCheckerPlugin()) { bool retVal = false; + bool success = QMetaObject::invokeMethod(plugin, - "checkCommunityLicense", + "evaluationLicense", Qt::DirectConnection, Q_RETURN_ARG(bool, retVal)); - if (success) - return retVal; + if (success && retVal) + return enterprise; + + retVal = false; + + success = QMetaObject::invokeMethod(plugin, + "qdsEnterpriseLicense", + Qt::DirectConnection, + Q_RETURN_ARG(bool, retVal)); + if (success && retVal) + return enterprise; + else + return professional; } - - if (Internal::dsLicenseCheckerPluginExists()) - return false; - - return true; + if (checkEnterpriseLicense()) + return enterprise; + return community; } } // namespace Utils From 070e482138951045d7d1b75b28d5ab137248d4c0 Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Thu, 29 Feb 2024 17:39:29 +0100 Subject: [PATCH 115/176] Nanotrace: build doc only does not call any add_qtc_library So it would fail to find the Nanotrace target. Change-Id: I1ff8be19e930bd547ced37767be2042f8261e75b Reviewed-by: Marco Bubke Reviewed-by: Qt CI Patch Build Bot --- src/libs/nanotrace/CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libs/nanotrace/CMakeLists.txt b/src/libs/nanotrace/CMakeLists.txt index baad068ca86..011c26f0492 100644 --- a/src/libs/nanotrace/CMakeLists.txt +++ b/src/libs/nanotrace/CMakeLists.txt @@ -10,7 +10,9 @@ add_qtc_library(Nanotrace VISIBILITY_INLINES_HIDDEN OFF ) -target_compile_options(Nanotrace PUBLIC $<$:/wd5030>) +if(TARGET Nanotrace) + target_compile_options(Nanotrace PUBLIC $<$:/wd5030>) +endif() option(DESIGNSTUDIO_USE_NANOTRACE "Enables collecting performance data with nanotrace for Design Studio" OFF) From ad0c560d138d70147549baedfc6d45a1df997524 Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Thu, 29 Feb 2024 19:05:10 +0100 Subject: [PATCH 116/176] qds: add EffectComposer in EffectComposer.json.in, wie have "DisabledByDefault" : true, so we need to ForceEnabled it Task-number: QDS-12115 Change-Id: Iec89a38a518d3dada285a38d304485dfcd71f979 Reviewed-by: Tim Jenssen --- dist/branding/qtdesignstudio/QtProject/QtDesignStudio.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/dist/branding/qtdesignstudio/QtProject/QtDesignStudio.ini b/dist/branding/qtdesignstudio/QtProject/QtDesignStudio.ini index a48e5ded50a..d87ff0d80b1 100644 --- a/dist/branding/qtdesignstudio/QtProject/QtDesignStudio.ini +++ b/dist/branding/qtdesignstudio/QtProject/QtDesignStudio.ini @@ -1,6 +1,7 @@ [Plugins] Ignored=AutoTest, Bazaar, ClangCodeModel, ClangTools, CMakeProjectManager, CVS, ClassView, CodePaster, CppEditor, CtfVisualizer, Designer, FakeVim, GLSLEditor, GenericProjectManager, IncrediBuild, Ios, Macros, Mercurial, ModelEditor, Perforce, PerfProfiler, ScxmlEditor, QbsProjectManager, Qnx, Subversion, Valgrind, VcsBase, Welcome, WinRt, Python ForceEnabled=Boot2Qt, StudioWelcome, QmlDesigner, ModuleTools, McuSupport +ForceEnabled=Boot2Qt, StudioWelcome, QmlDesigner, ModuleTools, McuSupport, EffectComposer [Core] NewDialog\LastCategory=H.StudioProject From 443c3477ab8c7536ddbb0e9c4827d2f126258c84 Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Thu, 29 Feb 2024 19:05:54 +0100 Subject: [PATCH 117/176] qds: no need to ignore not built plugins Change-Id: I65f170aa6efe3f7060e2f87363b66e89bd4e76cb Reviewed-by: Tim Jenssen --- dist/branding/qtdesignstudio/QtProject/QtDesignStudio.ini | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dist/branding/qtdesignstudio/QtProject/QtDesignStudio.ini b/dist/branding/qtdesignstudio/QtProject/QtDesignStudio.ini index d87ff0d80b1..70dec8082df 100644 --- a/dist/branding/qtdesignstudio/QtProject/QtDesignStudio.ini +++ b/dist/branding/qtdesignstudio/QtProject/QtDesignStudio.ini @@ -1,6 +1,5 @@ [Plugins] -Ignored=AutoTest, Bazaar, ClangCodeModel, ClangTools, CMakeProjectManager, CVS, ClassView, CodePaster, CppEditor, CtfVisualizer, Designer, FakeVim, GLSLEditor, GenericProjectManager, IncrediBuild, Ios, Macros, Mercurial, ModelEditor, Perforce, PerfProfiler, ScxmlEditor, QbsProjectManager, Qnx, Subversion, Valgrind, VcsBase, Welcome, WinRt, Python -ForceEnabled=Boot2Qt, StudioWelcome, QmlDesigner, ModuleTools, McuSupport +Ignored= ForceEnabled=Boot2Qt, StudioWelcome, QmlDesigner, ModuleTools, McuSupport, EffectComposer [Core] From 9f024eb5cd7abc95a462506ebea9274d0cdd3939 Mon Sep 17 00:00:00 2001 From: Teea Poldsam Date: Fri, 1 Mar 2024 14:38:19 +0200 Subject: [PATCH 118/176] Doc: Fix broken links to keyboard shortcuts Fixed the broken links to keyboard shortcuts. Fixes: QDS-12122 Change-Id: I461bbe2aa8f30e8f7fea202b15d7e66cfa23083f Reviewed-by: Leena Miettinen --- doc/qtcreator/src/howto/creator-keyboard-preferences.qdoc | 6 +++--- doc/qtcreator/src/howto/creator-sessions.qdoc | 2 +- doc/qtdesignstudio/src/qtdesignstudio-projects.qdoc | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/qtcreator/src/howto/creator-keyboard-preferences.qdoc b/doc/qtcreator/src/howto/creator-keyboard-preferences.qdoc index ef59bdf6d2f..66eab4e7858 100644 --- a/doc/qtcreator/src/howto/creator-keyboard-preferences.qdoc +++ b/doc/qtcreator/src/howto/creator-keyboard-preferences.qdoc @@ -10,7 +10,7 @@ /*! \page creator-how-to-assign-keyboard-shortcuts.html \if defined(qtdesignstudio) - \previouspage qtdesignstudio-keyboard-shortcuts.html + \previouspage studio-keyboard-shortcuts.html \nextpage studio-projects.html \else \previouspage creator-how-tos.html @@ -70,7 +70,7 @@ /*! \page creator-how-to-find-keyboard-shortcuts.html \if defined(qtdesignstudio) - \previouspage qtdesignstudio-keyboard-shortcuts.html + \previouspage studio-keyboard-shortcuts.html \nextpage studio-projects.html \else \previouspage creator-how-tos.html @@ -103,7 +103,7 @@ /*! \page creator-how-to-change-keyboard-shortcuts.html \if defined(qtdesignstudio) - \previouspage qtdesignstudio-keyboard-shortcuts.html + \previouspage studio-keyboard-shortcuts.html \nextpage studio-projects.html \else \previouspage creator-how-tos.html diff --git a/doc/qtcreator/src/howto/creator-sessions.qdoc b/doc/qtcreator/src/howto/creator-sessions.qdoc index 646e4fbc330..a2a84550402 100644 --- a/doc/qtcreator/src/howto/creator-sessions.qdoc +++ b/doc/qtcreator/src/howto/creator-sessions.qdoc @@ -11,7 +11,7 @@ \page creator-project-managing-sessions.html \if defined(qtdesignstudio) \previouspage creator-project-managing-workspaces.html - \nextpage qtdesignstudio-keyboard-shortcuts.html + \nextpage studio-keyboard-shortcuts.html \else \previouspage creator-how-tos.html \nextpage creator-keyboard-shortcuts.html diff --git a/doc/qtdesignstudio/src/qtdesignstudio-projects.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-projects.qdoc index 639b4d45fd9..3daf6e7089d 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-projects.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-projects.qdoc @@ -3,7 +3,7 @@ /*! \page studio-projects.html - \previouspage qtdesignstudio-keyboard-shortcuts.html + \previouspage studio-keyboard-shortcuts.html \nextpage studio-use-cases.html \title Creating Projects From f7530458c456e15d318578b1c18435409936d319 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Thu, 29 Feb 2024 23:46:39 +0200 Subject: [PATCH 119/176] QmlDesigner: Remove CollectionSourceModel Task-number: QDS-12032 Change-Id: Id0b999500a0a8cae16a90a59d051cd725417e431 Reviewed-by: Mahmoud Badri Reviewed-by: Miikka Heikkinen --- .../CollectionItem.qml | 18 +- .../CollectionListView.qml | 17 + .../CollectionView.qml | 27 +- .../ModelSourceItem.qml | 36 - src/plugins/qmldesigner/CMakeLists.txt | 1 - .../collectioneditorutils.cpp | 9 - .../collectioneditor/collectioneditorutils.h | 2 - .../collectioneditor/collectionlistmodel.cpp | 369 ++++++++- .../collectioneditor/collectionlistmodel.h | 36 +- .../collectionsourcemodel.cpp | 738 ------------------ .../collectioneditor/collectionsourcemodel.h | 119 --- .../collectioneditor/collectionview.cpp | 120 +-- .../collectioneditor/collectionview.h | 17 +- .../collectioneditor/collectionwidget.cpp | 14 +- .../collectioneditor/collectionwidget.h | 6 +- .../collectioneditor/datastoremodelnode.cpp | 22 +- .../collectioneditor/datastoremodelnode.h | 3 +- 17 files changed, 459 insertions(+), 1095 deletions(-) create mode 100644 share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionListView.qml delete mode 100644 share/qtcreator/qmldesigner/collectionEditorQmlSource/ModelSourceItem.qml delete mode 100644 src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.cpp delete mode 100644 src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.h diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionItem.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionItem.qml index e5ba5a96e5e..31ced43c1dd 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionItem.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionItem.qml @@ -7,6 +7,7 @@ import QtQuick.Layouts import HelperWidgets 2.0 as HelperWidgets import StudioControls 1.0 as StudioControls import StudioTheme as StudioTheme +import CollectionEditorBackend Item { id: root @@ -15,8 +16,6 @@ Item { implicitHeight: boundingRect.height + 3 property color textColor - property string sourceType - property bool hasSelectedTarget signal selectItem(int itemIndex) signal deleteItem() @@ -116,7 +115,7 @@ Item { StudioControls.MenuItem { text: qsTr("Assign to the selected node") - enabled: root.hasSelectedTarget + enabled: CollectionEditorBackend.rootView.targetNodeSelected onTriggered: rootView.assignCollectionToSelectedNode(collectionName) } } @@ -141,16 +140,9 @@ Item { wrapMode: Text.WordWrap color: StudioTheme.Values.themeTextColor - text: { - if (root.sourceType === "json") { - qsTr("Are you sure that you want to delete model \"%1\"?" - + "\nThe model will be deleted permanently.").arg(collectionName) - } else if (root.sourceType === "csv") { - qsTr("Are you sure that you want to delete model \"%1\"?" - + "\nThe model will be removed from the project " - + "but the file will not be deleted.").arg(collectionName) - } - } + text: qsTr("Are you sure that you want to delete model \"%1\"?" + + "\nThe model will be deleted permanently.").arg(collectionName) + } Spacer {} diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionListView.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionListView.qml new file mode 100644 index 00000000000..1cde74fe375 --- /dev/null +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionListView.qml @@ -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 + +import QtQuick +import CollectionEditorBackend + +ListView { + id: root + + model: CollectionEditorBackend.model + clip: true + + delegate: CollectionItem { + implicitWidth: parent.width + onDeleteItem: root.model.removeRow(index) + } +} diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml index 3c393b3387d..b9f8d9a532f 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml @@ -144,34 +144,13 @@ Item { } } - Item { // Model Groups + CollectionListView { // Model Groups Layout.fillWidth: true Layout.minimumHeight: bottomSpacer.isExpanded ? 150 : 0 Layout.fillHeight: !bottomSpacer.isExpanded - Layout.preferredHeight: sourceListView.contentHeight - Layout.maximumHeight: sourceListView.contentHeight + Layout.preferredHeight: contentHeight + Layout.maximumHeight: contentHeight Layout.alignment: Qt.AlignTop | Qt.AlignHCenter - - MouseArea { - anchors.fill: parent - propagateComposedEvents: true - onClicked: (event) => { - root.model.deselect() - event.accepted = true - } - } - - ListView { - id: sourceListView - - anchors.fill: parent - model: root.model - - delegate: ModelSourceItem { - implicitWidth: sourceListView.width - hasSelectedTarget: root.rootView.targetNodeSelected - } - } } HelperWidgets.IconButton { diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/ModelSourceItem.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/ModelSourceItem.qml deleted file mode 100644 index 8872bb0d041..00000000000 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/ModelSourceItem.qml +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -import QtQuick -import QtQuick.Controls -import QtQuick.Layouts -import HelperWidgets 2.0 as HelperWidgets -import StudioControls 1.0 as StudioControls -import StudioTheme as StudioTheme - -Item { - id: root - - implicitWidth: 300 - implicitHeight: collectionListView.height - - property bool hasSelectedTarget - - ListView { - id: collectionListView - - width: parent.width - implicitHeight: contentHeight - leftMargin: 0 - - model: internalModels - clip: true - - delegate: CollectionItem { - width: collectionListView.width - sourceType: collectionListView.model.sourceType - hasSelectedTarget: root.hasSelectedTarget - onDeleteItem: collectionListView.model.removeRow(index) - } - } -} diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index aa4750f81d1..5498097c1dd 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -838,7 +838,6 @@ extend_qtc_plugin(QmlDesigner collectioneditorconstants.h collectioneditorutils.cpp collectioneditorutils.h collectionlistmodel.cpp collectionlistmodel.h - collectionsourcemodel.cpp collectionsourcemodel.h collectionview.cpp collectionview.h collectionwidget.cpp collectionwidget.h datastoremodelnode.cpp datastoremodelnode.h diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp index ecc2544bcc3..e1a17c51a15 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp @@ -176,15 +176,6 @@ bool variantIslessThan(const QVariant &a, const QVariant &b, DataType type) return std::visit(LessThanVisitor{}, valueToVariant(a, type), valueToVariant(b, type)); } -CollectionEditorConstants::SourceFormat getSourceCollectionFormat(const ModelNode &node) -{ - using namespace QmlDesigner; - if (node.type() == CollectionEditorConstants::JSONCOLLECTIONMODEL_TYPENAME) - return CollectionEditorConstants::SourceFormat::Json; - - return CollectionEditorConstants::SourceFormat::Unknown; -} - QString getSourceCollectionType(const ModelNode &node) { using namespace QmlDesigner; diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h index bd9e24d23c6..5be1c51f001 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h @@ -19,8 +19,6 @@ namespace QmlDesigner::CollectionEditorUtils { bool variantIslessThan(const QVariant &a, const QVariant &b, CollectionDetails::DataType type); -CollectionEditorConstants::SourceFormat getSourceCollectionFormat(const QmlDesigner::ModelNode &node); - QString getSourceCollectionType(const QmlDesigner::ModelNode &node); QString getSourceCollectionPath(const QmlDesigner::ModelNode &dataStoreNode); diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.cpp index 323f0e767fa..d27a077d2a7 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.cpp @@ -6,8 +6,13 @@ #include "collectioneditorutils.h" #include +#include #include +#include +#include +#include + namespace { template @@ -20,14 +25,23 @@ bool containsItem(const std::initializer_list &container, const Value return it != end; } +bool sameCollectionNames(QStringList a, QStringList b) +{ + if (a.size() != b.size()) + return false; + + a.sort(Qt::CaseSensitive); + b.sort(Qt::CaseSensitive); + + return a == b; +} + } // namespace namespace QmlDesigner { -CollectionListModel::CollectionListModel(const ModelNode &sourceModel) +CollectionListModel::CollectionListModel() : QAbstractListModel() - , m_sourceNode(sourceModel) - , m_sourceType(CollectionEditorUtils::getSourceCollectionType(sourceModel)) { connect(this, &CollectionListModel::modelReset, this, &CollectionListModel::updateEmpty); connect(this, &CollectionListModel::rowsRemoved, this, &CollectionListModel::updateEmpty); @@ -59,18 +73,26 @@ bool CollectionListModel::setData(const QModelIndex &index, const QVariant &valu return false; if (containsItem({Qt::EditRole, Qt::DisplayRole, NameRole}, role)) { - if (contains(value.toString())) + if (collectionExists(value.toString())) return false; QString oldName = collectionNameAt(index.row()); bool nameChanged = value != data(index); if (nameChanged) { QString newName = value.toString(); - m_data.replace(index.row(), newName); - emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole}); - emit this->collectionNameChanged(oldName, newName); + QString errorString; + if (renameCollectionInDataStore(oldName, newName, errorString)) { + m_data.replace(index.row(), newName); + emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole, NameRole}); + emit this->collectionNameChanged(oldName, newName); + if (m_selectedCollectionName == oldName) + updateSelectedCollectionName(); + return true; + } else { + emit warning("Rename Model", errorString); + return false; + } } - return nameChanged; } else if (role == SelectedRole) { if (value.toBool() != index.data(SelectedRole).toBool()) { setSelectedIndex(value.toBool() ? index.row() : -1); @@ -92,17 +114,27 @@ bool CollectionListModel::removeRows(int row, int count, const QModelIndex &pare if (count < 1) return false; + QString errorString; QStringList removedCollections = m_data.mid(row, count); + if (removeCollectionsFromDataStore(removedCollections, errorString)) { + beginRemoveRows(parent, row, row + count - 1); + m_data.remove(row, count); + endRemoveRows(); - beginRemoveRows(parent, row, row + count - 1); - m_data.remove(row, count); - endRemoveRows(); + emit collectionsRemoved(removedCollections); + if (m_selectedIndex >= row) { + int preferredIndex = m_selectedIndex - count; + if (preferredIndex < 0) // If the selected item is deleted, reset selection + selectCollectionIndex(-1); + selectCollectionIndex(preferredIndex, true); + } - emit collectionsRemoved(removedCollections); - if (m_selectedIndex >= row) - selectCollectionIndex(m_selectedIndex - count, true); - - return true; + updateSelectedCollectionName(); + return true; + } else { + emit warning("Remove Model", errorString); + return false; + } } QVariant CollectionListModel::data(const QModelIndex &index, int role) const @@ -120,13 +152,10 @@ QVariant CollectionListModel::data(const QModelIndex &index, int role) const } } -void CollectionListModel::resetModelData(const QStringList &collectionsList) +void CollectionListModel::setDataStoreNode(const ModelNode &dataStoreNode) { - QString prevSelectedCollection = selectedIndex() > -1 ? m_data.at(selectedIndex()) : QString(); - beginResetModel(); - m_data = collectionsList; - endResetModel(); - selectCollectionName(prevSelectedCollection); + m_dataStoreNode = dataStoreNode; + update(); } int CollectionListModel::selectedIndex() const @@ -136,15 +165,10 @@ int CollectionListModel::selectedIndex() const ModelNode CollectionListModel::sourceNode() const { - return m_sourceNode; + return m_dataStoreNode; } -QString CollectionListModel::sourceAddress() const -{ - return CollectionEditorUtils::getSourceCollectionPath(m_sourceNode); -} - -bool CollectionListModel::contains(const QString &collectionName) const +bool CollectionListModel::collectionExists(const QString &collectionName) const { return m_data.contains(collectionName); } @@ -154,6 +178,19 @@ QStringList CollectionListModel::collections() const return m_data; } +QString CollectionListModel::getUniqueCollectionName(const QString &baseName) const +{ + QString name = baseName.isEmpty() ? "Model" : baseName; + QString nameTemplate = name + "%1"; + + int num = 0; + + while (collectionExists(name)) + name = nameTemplate.arg(++num, 2, 10, QChar('0')); + + return name; +} + void CollectionListModel::selectCollectionIndex(int idx, bool selectAtLeastOne) { int collectionCount = m_data.size(); @@ -168,11 +205,20 @@ void CollectionListModel::selectCollectionIndex(int idx, bool selectAtLeastOne) setSelectedIndex(preferredIndex); } -void CollectionListModel::selectCollectionName(const QString &collectionName) +void CollectionListModel::selectCollectionName(QString collectionName, bool selectAtLeastOne) { int idx = m_data.indexOf(collectionName); if (idx > -1) selectCollectionIndex(idx); + else + selectCollectionIndex(selectedIndex(), selectAtLeastOne); + + collectionName = collectionNameAt(selectedIndex()); + if (m_selectedCollectionName == collectionName) + return; + + m_selectedCollectionName = collectionName; + emit selectedCollectionNameChanged(m_selectedCollectionName); } QString CollectionListModel::collectionNameAt(int idx) const @@ -180,17 +226,76 @@ QString CollectionListModel::collectionNameAt(int idx) const return index(idx).data(NameRole).toString(); } -void CollectionListModel::addCollection(const QString &collectionName) +QString CollectionListModel::selectedCollectionName() const { - if (m_data.contains(collectionName)) - return; + return m_selectedCollectionName; +} - int row = rowCount(); - beginInsertRows({}, row, row); - m_data.append(collectionName); - endInsertRows(); +void CollectionListModel::update() +{ + using Utils::FilePath; + using Utils::FileReader; - emit collectionAdded(collectionName); + FileReader sourceFile; + QString sourceFileAddress = CollectionEditorUtils::getSourceCollectionPath(m_dataStoreNode); + FilePath path = FilePath::fromUserInput(sourceFileAddress); + bool fileRead = false; + if (path.exists()) { + fileRead = sourceFile.fetch(path); + if (!fileRead) + emit this->warning(tr("Model Editor"), + tr("Cannot read the dataStore file\n%1").arg(sourceFile.errorString())); + } + + QStringList collectionNames; + if (fileRead) { + QJsonParseError parseError; + QJsonDocument document = QJsonDocument::fromJson(sourceFile.data(), &parseError); + if (parseError.error != QJsonParseError::NoError) { + emit this->warning(tr("Model Editor"), + tr("There is an error in the JSON file.\n%1") + .arg(parseError.errorString())); + } else { + if (document.isObject()) + collectionNames = document.object().toVariantMap().keys(); + else + emit this->warning(tr("Model Editor"), tr("The JSON document be an object.")); + } + } + + if (!sameCollectionNames(m_data, collectionNames)) { + QString prevSelectedCollection = selectedIndex() > -1 ? m_data.at(selectedIndex()) + : QString(); + beginResetModel(); + m_data = collectionNames; + endResetModel(); + emit this->collectionNamesChanged(collections()); + selectCollectionName(prevSelectedCollection, true); + } +} + +bool CollectionListModel::addCollection(const QString &collectionName, + const QJsonObject &localCollection) +{ + if (collectionExists(collectionName)) { + emit warning(tr("Add Model"), tr("Model \"%1\" already exists.").arg(collectionName)); + return false; + } + + QString errorMessage; + if (addCollectionToDataStore(collectionName, localCollection, errorMessage)) { + int row = rowCount(); + beginInsertRows({}, row, row); + m_data.append(collectionName); + endInsertRows(); + + selectCollectionName(collectionName); + emit collectionAdded(collectionName); + return true; + } else { + emit warning(tr("Add Collection"), errorMessage); + } + return false; } void CollectionListModel::setSelectedIndex(int idx) @@ -210,6 +315,187 @@ void CollectionListModel::setSelectedIndex(int idx) emit dataChanged(newIndex, newIndex, {SelectedRole}); emit selectedIndexChanged(idx); + updateSelectedCollectionName(); + } +} + +bool CollectionListModel::removeCollectionsFromDataStore(const QStringList &removedCollections, + QString &error) const +{ + using Utils::FilePath; + using Utils::FileReader; + auto setErrorAndReturn = [&error](const QString &msg) -> bool { + error = msg; + return false; + }; + + if (m_dataStoreNode.type() != CollectionEditorConstants::JSONCOLLECTIONMODEL_TYPENAME) + return setErrorAndReturn(tr("Invalid node type")); + + QString sourceFileAddress = CollectionEditorUtils::getSourceCollectionPath(m_dataStoreNode); + + QFileInfo sourceFileInfo(sourceFileAddress); + if (!sourceFileInfo.isFile()) + return setErrorAndReturn(tr("The selected node has an invalid source address")); + + FilePath jsonPath = FilePath::fromUserInput(sourceFileAddress); + FileReader jsonFile; + if (!jsonFile.fetch(jsonPath)) { + return setErrorAndReturn(tr("Can't read file \"%1\".\n%2") + .arg(sourceFileInfo.absoluteFilePath(), jsonFile.errorString())); + } + + QJsonParseError parseError; + QJsonDocument document = QJsonDocument::fromJson(jsonFile.data(), &parseError); + if (parseError.error != QJsonParseError::NoError) { + return setErrorAndReturn(tr("\"%1\" is corrupted.\n%2") + .arg(sourceFileInfo.absoluteFilePath(), parseError.errorString())); + } + + if (document.isObject()) { + QJsonObject rootObject = document.object(); + + for (const QString &collectionName : removedCollections) { + bool sourceContainsCollection = rootObject.contains(collectionName); + if (sourceContainsCollection) { + rootObject.remove(collectionName); + } else { + setErrorAndReturn(tr("The model group doesn't contain the model name (%1).") + .arg(sourceContainsCollection)); + } + } + + document.setObject(rootObject); + + if (CollectionEditorUtils::writeToJsonDocument(jsonPath, document)) { + error.clear(); + return true; + } else { + return setErrorAndReturn( + tr("Can't write to \"%1\".").arg(sourceFileInfo.absoluteFilePath())); + } + } else { + return setErrorAndReturn(tr("Local Json Document should be an object")); + } + + return false; +} + +bool CollectionListModel::renameCollectionInDataStore(const QString &oldName, + const QString &newName, + QString &error) +{ + using Utils::FilePath; + using Utils::FileReader; + using Utils::FileSaver; + + auto setErrorAndReturn = [&error](const QString &msg) -> bool { + error = msg; + return false; + }; + + if (m_dataStoreNode.type() != CollectionEditorConstants::JSONCOLLECTIONMODEL_TYPENAME) + return setErrorAndReturn(tr("Invalid node type")); + + QString sourceFileAddress = CollectionEditorUtils::getSourceCollectionPath(m_dataStoreNode); + + QFileInfo sourceFileInfo(sourceFileAddress); + if (!sourceFileInfo.isFile()) + return setErrorAndReturn(tr("Selected node must have a valid source file address")); + + FilePath jsonPath = FilePath::fromUserInput(sourceFileAddress); + FileReader jsonFile; + if (!jsonFile.fetch(jsonPath)) { + return setErrorAndReturn( + tr("Can't read \"%1\".\n%2").arg(sourceFileInfo.absoluteFilePath(), jsonFile.errorString())); + } + + QJsonParseError parseError; + QJsonDocument document = QJsonDocument::fromJson(jsonFile.data(), &parseError); + if (parseError.error != QJsonParseError::NoError) { + return setErrorAndReturn(tr("\"%1\" is corrupted.\n%2") + .arg(sourceFileInfo.absoluteFilePath(), parseError.errorString())); + } + + if (document.isObject()) { + QJsonObject rootObject = document.object(); + + bool collectionContainsOldName = rootObject.contains(oldName); + bool collectionContainsNewName = rootObject.contains(newName); + + if (!collectionContainsOldName) { + return setErrorAndReturn( + tr("The model group doesn't contain the old model name (%1).").arg(oldName)); + } + + if (collectionContainsNewName) { + return setErrorAndReturn( + tr("The model name \"%1\" already exists in the model group.").arg(newName)); + } + + QJsonValue oldValue = rootObject.value(oldName); + rootObject.insert(newName, oldValue); + rootObject.remove(oldName); + + document.setObject(rootObject); + + if (CollectionEditorUtils::writeToJsonDocument(jsonPath, document)) { + error.clear(); + return true; + } else { + return setErrorAndReturn( + tr("Can't write to \"%1\".").arg(sourceFileInfo.absoluteFilePath())); + } + } else { + return setErrorAndReturn(tr("Local Json Document should be an object")); + } + return false; +} + +bool CollectionListModel::addCollectionToDataStore(const QString &collectionName, + const QJsonObject &localCollection, + QString &errorString) const +{ + using Utils::FilePath; + using Utils::FileReader; + auto returnError = [&errorString](const QString &msg) -> bool { + errorString = msg; + return false; + }; + + if (collectionExists(collectionName)) + return returnError(tr("A model with the identical name already exists.")); + + QString sourceFileAddress = CollectionEditorUtils::getSourceCollectionPath(m_dataStoreNode); + + QFileInfo sourceFileInfo(sourceFileAddress); + if (!sourceFileInfo.isFile()) + return returnError(tr("Selected node must have a valid source file address")); + + FilePath jsonPath = FilePath::fromUserInput(sourceFileAddress); + FileReader jsonFile; + if (!jsonFile.fetch(jsonPath)) { + return returnError( + tr("Can't read \"%1\".\n%2").arg(sourceFileInfo.absoluteFilePath(), jsonFile.errorString())); + } + + QJsonParseError parseError; + QJsonDocument document = QJsonDocument::fromJson(jsonFile.data(), &parseError); + if (parseError.error != QJsonParseError::NoError) + return returnError(tr("\"%1\" is corrupted.\n%2") + .arg(sourceFileInfo.absoluteFilePath(), parseError.errorString())); + + if (document.isObject()) { + QJsonObject sourceObject = document.object(); + sourceObject.insert(collectionName, localCollection); + document.setObject(sourceObject); + + if (CollectionEditorUtils::writeToJsonDocument(jsonPath, document)) + return true; + else + return returnError(tr("Can't write to \"%1\".").arg(sourceFileInfo.absoluteFilePath())); + } else { + return returnError(tr("JSON document type should be an object containing models.")); } } @@ -225,4 +511,11 @@ void CollectionListModel::updateEmpty() } } +void CollectionListModel::updateSelectedCollectionName() +{ + QString selectedCollectionByIndex = collectionNameAt(selectedIndex()); + if (selectedCollectionByIndex != selectedCollectionName()) + selectCollectionName(selectedCollectionByIndex); +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.h b/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.h index 092dd1d6dc7..7902fd59097 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.h @@ -16,49 +16,63 @@ class CollectionListModel : public QAbstractListModel Q_PROPERTY(int selectedIndex MEMBER m_selectedIndex NOTIFY selectedIndexChanged) Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged) - Q_PROPERTY(QString sourceType MEMBER m_sourceType CONSTANT) + Q_PROPERTY(QString selectedCollectionName + READ selectedCollectionName + WRITE selectCollectionName + NOTIFY selectedCollectionNameChanged) public: - enum Roles { IdRole = Qt::UserRole + 1, NameRole, SourceRole, SelectedRole, CollectionsRole }; + enum Roles { IdRole = Qt::UserRole + 1, NameRole, SelectedRole }; - explicit CollectionListModel(const ModelNode &sourceModel); + explicit CollectionListModel(); QHash roleNames() const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; bool setData(const QModelIndex &index, const QVariant &value, int role) override; bool removeRows(int row, int count, const QModelIndex &parent = {}) override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; - void resetModelData(const QStringList &collectionsList); + void setDataStoreNode(const ModelNode &dataStoreNode = {}); Q_INVOKABLE int selectedIndex() const; Q_INVOKABLE ModelNode sourceNode() const; - Q_INVOKABLE QString sourceAddress() const; - Q_INVOKABLE bool contains(const QString &collectionName) const; + Q_INVOKABLE bool collectionExists(const QString &collectionName) const; Q_INVOKABLE QStringList collections() const; + Q_INVOKABLE QString getUniqueCollectionName(const QString &baseName = {}) const; void selectCollectionIndex(int idx, bool selectAtLeastOne = false); - void selectCollectionName(const QString &collectionName); + void selectCollectionName(QString collectionName, bool selectAtLeastOne = false); QString collectionNameAt(int idx) const; - void addCollection(const QString &collectionName); + QString selectedCollectionName() const; + + void update(); + bool addCollection(const QString &collectionName, const QJsonObject &localCollection); signals: void selectedIndexChanged(int idx); void isEmptyChanged(bool); void collectionNameChanged(const QString &oldName, const QString &newName); + void collectionNamesChanged(const QStringList &collectionNames); void collectionsRemoved(const QStringList &names); void collectionAdded(const QString &name); + void selectedCollectionNameChanged(const QString &selectedCollectionName); + void warning(const QString &title, const QString &body); private: void setSelectedIndex(int idx); + bool removeCollectionsFromDataStore(const QStringList &removedCollections, QString &error) const; + bool renameCollectionInDataStore(const QString &oldName, const QString &newName, QString &error); + bool addCollectionToDataStore(const QString &collectionName, + const QJsonObject &localCollection, + QString &errorString) const; void updateEmpty(); + void updateSelectedCollectionName(); using Super = QAbstractListModel; int m_selectedIndex = -1; bool m_isEmpty = false; - const ModelNode m_sourceNode; - const QString m_sourceType; - + ModelNode m_dataStoreNode; + QString m_selectedCollectionName; QStringList m_data; }; diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.cpp deleted file mode 100644 index 7760f46ff16..00000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.cpp +++ /dev/null @@ -1,738 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "collectionsourcemodel.h" - -#include "abstractview.h" -#include "collectioneditorconstants.h" -#include "collectioneditorutils.h" -#include "collectionlistmodel.h" -#include "variantproperty.h" - -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -namespace { - -QSharedPointer loadCollection( - const QmlDesigner::ModelNode &sourceNode, - QSharedPointer initialCollection = {}) -{ - using namespace QmlDesigner::CollectionEditorConstants; - using namespace QmlDesigner::CollectionEditorUtils; - using Utils::FilePath; - using Utils::FileReader; - QString sourceFileAddress = getSourceCollectionPath(sourceNode); - - QSharedPointer collectionsList; - auto setupCollectionList = [&sourceNode, &initialCollection, &collectionsList]() { - if (initialCollection.isNull()) - collectionsList.reset(new QmlDesigner::CollectionListModel(sourceNode)); - else if (initialCollection->sourceNode() == sourceNode) - collectionsList = initialCollection; - else - collectionsList.reset(new QmlDesigner::CollectionListModel(sourceNode)); - }; - - if (sourceNode.type() == JSONCOLLECTIONMODEL_TYPENAME) { - FileReader sourceFile; - if (!sourceFile.fetch(FilePath::fromUserInput(sourceFileAddress))) - return {}; - - QJsonParseError parseError; - QJsonDocument document = QJsonDocument::fromJson(sourceFile.data(), &parseError); - if (parseError.error != QJsonParseError::NoError) - return {}; - - setupCollectionList(); - - if (document.isObject()) { - const QJsonObject sourceObject = document.object(); - collectionsList->resetModelData(sourceObject.toVariantMap().keys()); - } - } - - return collectionsList; -} - -} // namespace - -namespace QmlDesigner { - -CollectionSourceModel::CollectionSourceModel(QObject *parent) - : Super(parent) -{} - -int CollectionSourceModel::rowCount(const QModelIndex &) const -{ - return m_collectionSources.size(); -} - -QVariant CollectionSourceModel::data(const QModelIndex &index, int role) const -{ - QTC_ASSERT(index.isValid(), return {}); - - const ModelNode *collectionSource = &m_collectionSources.at(index.row()); - - switch (role) { - case NameRole: // Not used, to be removed - return collectionSource->variantProperty("objectName").value().toString(); - case NodeRole: - return QVariant::fromValue(*collectionSource); - case CollectionTypeRole: - return CollectionEditorUtils::getSourceCollectionType(*collectionSource); - case SourceRole: - return collectionSource->variantProperty(CollectionEditorConstants::SOURCEFILE_PROPERTY).value(); - case SelectedRole: - return index.row() == m_selectedIndex; - case CollectionsRole: - return QVariant::fromValue(m_collectionList.at(index.row()).data()); - } - - return {}; -} - -bool CollectionSourceModel::setData(const QModelIndex &index, const QVariant &value, int role) -{ - if (!index.isValid()) - return false; - - ModelNode collectionSource = m_collectionSources.at(index.row()); - switch (role) { - case Qt::DisplayRole: - case NameRole: { - auto collectionName = collectionSource.variantProperty("objectName"); - if (collectionName.value() == value) - return false; - - collectionName.setValue(value.toString()); - } break; - case SourceRole: { - auto sourceAddress = collectionSource.variantProperty( - CollectionEditorConstants::SOURCEFILE_PROPERTY); - if (sourceAddress.value() == value) - return false; - - sourceAddress.setValue(value.toString()); - } break; - case SelectedRole: { - if (value.toBool() != index.data(SelectedRole).toBool()) - setSelectedIndex(value.toBool() ? index.row() : -1); - else - return false; - } break; - default: - return false; - } - - return true; -} - -bool CollectionSourceModel::removeRows(int row, int count, [[maybe_unused]] const QModelIndex &parent) -{ - const int rowMax = std::min(row + count, rowCount()); - - if (row >= rowMax || row < 0) - return false; - - AbstractView *view = m_collectionSources.at(row).view(); - if (!view) - return false; - - count = rowMax - row; - - bool selectionUpdateNeeded = m_selectedIndex >= row && m_selectedIndex < rowMax; - - // It's better to remove the group of nodes here because of the performance issue for the list, - // and update issue for the view - beginRemoveRows({}, row, rowMax - 1); - - view->executeInTransaction(Q_FUNC_INFO, [row, count, this]() { - for (ModelNode node : Utils::span(m_collectionSources).subspan(row, count)) { - m_sourceIndexHash.remove(node.internalId()); - node.destroy(); - } - m_collectionSources.remove(row, count); - m_collectionList.remove(row, count); - }); - - int idx = row; - for (const ModelNode &node : Utils::span(m_collectionSources).subspan(row)) - m_sourceIndexHash.insert(node.internalId(), ++idx); - - endRemoveRows(); - - if (selectionUpdateNeeded) - updateSelectedSource(); - - updateEmpty(); - return true; -} - -QHash CollectionSourceModel::roleNames() const -{ - static QHash roles; - if (roles.isEmpty()) { - roles.insert(Super::roleNames()); - roles.insert({{NameRole, "sourceName"}, - {NodeRole, "sourceNode"}, - {CollectionTypeRole, "sourceCollectionType"}, - {SelectedRole, "sourceIsSelected"}, - {SourceRole, "sourceAddress"}, - {CollectionsRole, "internalModels"}}); - } - return roles; -} - -void CollectionSourceModel::setSource(const ModelNode &source) -{ - beginResetModel(); - m_collectionSources = {source}; - m_sourceIndexHash.clear(); - m_collectionList.clear(); - - // TODO: change m_collectionSources to only contain 1 source node - m_sourceIndexHash.insert(source.internalId(), 0); - - auto loadedCollection = loadCollection(source); - m_collectionList.append(loadedCollection); - - registerCollectionList(loadedCollection); - - - updateEmpty(); - endResetModel(); - - updateSelectedSource(true); -} - -void CollectionSourceModel::reset() -{ - beginResetModel(); - m_collectionSources.clear(); - m_sourceIndexHash.clear(); - m_collectionList.clear(); - m_previousSelectedList.clear(); - setSelectedCollectionName({}); - - updateEmpty(); - endResetModel(); - updateSelectedSource(); -} - -int CollectionSourceModel::sourceIndex(const ModelNode &node) const -{ - return m_sourceIndexHash.value(node.internalId(), -1); -} - -void CollectionSourceModel::addSource(const ModelNode &node) -{ - int newRowId = m_collectionSources.count(); - beginInsertRows({}, newRowId, newRowId); - m_collectionSources.append(node); - m_sourceIndexHash.insert(node.internalId(), newRowId); - - auto loadedCollection = loadCollection(node); - m_collectionList.append(loadedCollection); - - registerCollectionList(loadedCollection); - - updateEmpty(); - endInsertRows(); - updateSelectedSource(true); -} - -void CollectionSourceModel::selectSource(const ModelNode &node) -{ - int nodePlace = m_sourceIndexHash.value(node.internalId(), -1); - if (nodePlace < 0) - return; - - selectSourceIndex(nodePlace, true); -} - -bool CollectionSourceModel::collectionExists(const QString &collectionName) const -{ - return m_collectionList.size() == 1 && m_collectionList.at(0)->contains(collectionName); -} - -bool CollectionSourceModel::addCollectionToSource(const ModelNode &node, - const QString &collectionName, - const QJsonObject &newCollection, - QString *errorString) -{ - using Utils::FilePath; - using Utils::FileReader; - auto returnError = [errorString](const QString &msg) -> bool { - if (errorString) - *errorString = msg; - return false; - }; - - int idx = sourceIndex(node); - if (idx < 0) - return returnError(tr("Node is not indexed in the models.")); - - if (node.type() != CollectionEditorConstants::JSONCOLLECTIONMODEL_TYPENAME) - return returnError(tr("Node should be a JSON model.")); - - if (collectionExists(collectionName)) - return returnError(tr("A model with the identical name already exists.")); - - QString sourceFileAddress = CollectionEditorUtils::getSourceCollectionPath(node); - - QFileInfo sourceFileInfo(sourceFileAddress); - if (!sourceFileInfo.isFile()) - return returnError(tr("Selected node must have a valid source file address")); - - FilePath jsonPath = FilePath::fromUserInput(sourceFileAddress); - FileReader jsonFile; - if (!jsonFile.fetch(jsonPath)) { - return returnError( - tr("Can't read \"%1\".\n%2").arg(sourceFileInfo.absoluteFilePath(), jsonFile.errorString())); - } - - QJsonParseError parseError; - QJsonDocument document = QJsonDocument::fromJson(jsonFile.data(), &parseError); - if (parseError.error != QJsonParseError::NoError) - return returnError(tr("\"%1\" is corrupted.\n%2") - .arg(sourceFileInfo.absoluteFilePath(), parseError.errorString())); - - if (document.isObject()) { - QJsonObject sourceObject = document.object(); - sourceObject.insert(collectionName, newCollection); - document.setObject(sourceObject); - - if (!CollectionEditorUtils::writeToJsonDocument(jsonPath, document)) - return returnError(tr("Can't write to \"%1\".").arg(sourceFileInfo.absoluteFilePath())); - - updateCollectionList(index(idx)); - - auto collections = m_collectionList.at(idx); - if (collections.isNull()) - return returnError(tr("No model is available for the JSON model group.")); - - collections->selectCollectionName(collectionName); - setSelectedCollectionName(collectionName); - return true; - } else { - return returnError(tr("JSON document type should be an object containing models.")); - } -} - -QmlDesigner::ModelNode CollectionSourceModel::sourceNodeAt(int idx) -{ - QModelIndex data = index(idx); - if (!data.isValid()) - return {}; - - return m_collectionSources.at(idx); -} - -CollectionListModel *CollectionSourceModel::selectedCollectionList() -{ - QModelIndex idx = index(m_selectedIndex); - if (!idx.isValid()) - return {}; - - return idx.data(CollectionsRole).value(); -} - -void CollectionSourceModel::selectSourceIndex(int idx, bool selectAtLeastOne) -{ - int collectionCount = m_collectionSources.size(); - int preferredIndex = -1; - if (collectionCount) { - if (selectAtLeastOne) - preferredIndex = std::max(0, std::min(idx, collectionCount - 1)); - else if (idx > -1 && idx < collectionCount) - preferredIndex = idx; - } - - setSelectedIndex(preferredIndex); -} - -void CollectionSourceModel::selectCollection(const QVariant &node, const QString &collectionName) -{ - const ModelNode sourceNode = node.value(); - const QModelIndex index = indexOfNode(sourceNode); - if (!index.isValid()) - return; - - selectSource(sourceNode); - auto collections = m_collectionList.at(index.row()); - if (collections.isNull()) - return; - - collections->selectCollectionName(collectionName); -} - -void CollectionSourceModel::deselect() -{ - setSelectedIndex(-1); -} - -void CollectionSourceModel::updateSelectedSource(bool selectAtLeastOne) -{ - int idx = m_selectedIndex; - m_selectedIndex = -1; - selectSourceIndex(idx, selectAtLeastOne); -} - -QString CollectionSourceModel::getUniqueCollectionName(const QString &baseName) const -{ - if (m_collectionList.isEmpty()) - return "Model01"; - - CollectionListModel *collectionModel = m_collectionList.at(0).data(); - - QString name = baseName.isEmpty() ? "Model" : baseName; - QString nameTemplate = name + "%1"; - - int num = 0; - - while (collectionModel->contains(name)) - name = nameTemplate.arg(++num, 2, 10, QChar('0')); - - return name; -} - -void CollectionSourceModel::updateNodeName(const ModelNode &node) -{ - QModelIndex index = indexOfNode(node); - emit dataChanged(index, index, {NameRole, Qt::DisplayRole}); - updateCollectionList(index); -} - -void CollectionSourceModel::updateNodeSource(const ModelNode &node) -{ - QModelIndex index = indexOfNode(node); - emit dataChanged(index, index, {SourceRole}); - updateCollectionList(index); -} - -void CollectionSourceModel::onSelectedCollectionChanged(CollectionListModel *collectionList, - int collectionIndex) -{ - if (collectionIndex > -1) { - if (m_previousSelectedList && m_previousSelectedList != collectionList) - m_previousSelectedList->selectCollectionIndex(-1); - - m_previousSelectedList = collectionList; - - setSelectedCollectionName(collectionList->collectionNameAt(collectionIndex)); - - selectSourceIndex(sourceIndex(collectionList->sourceNode())); - } else { - setSelectedCollectionName({}); - } -} - -void CollectionSourceModel::onCollectionNameChanged(CollectionListModel *collectionList, - const QString &oldName, - const QString &newName) -{ - using Utils::FilePath; - using Utils::FileReader; - using Utils::FileSaver; - - auto emitRenameWarning = [this](const QString &msg) -> void { - emit warning(tr("Rename Model"), msg); - }; - - const ModelNode node = collectionList->sourceNode(); - const QModelIndex nodeIndex = indexOfNode(node); - - if (!nodeIndex.isValid()) { - emitRenameWarning(tr("Invalid node")); - return; - } - - if (node.type() != CollectionEditorConstants::JSONCOLLECTIONMODEL_TYPENAME) { - emitRenameWarning(tr("Invalid node type")); - return; - } - - QString sourceFileAddress = CollectionEditorUtils::getSourceCollectionPath(node); - - QFileInfo sourceFileInfo(sourceFileAddress); - if (!sourceFileInfo.isFile()) { - emitRenameWarning(tr("Selected node must have a valid source file address")); - return; - } - - FilePath jsonPath = FilePath::fromUserInput(sourceFileAddress); - FileReader jsonFile; - if (!jsonFile.fetch(jsonPath)) { - emitRenameWarning( - tr("Can't read \"%1\".\n%2").arg(sourceFileInfo.absoluteFilePath(), jsonFile.errorString())); - return; - } - - QJsonParseError parseError; - QJsonDocument document = QJsonDocument::fromJson(jsonFile.data(), &parseError); - if (parseError.error != QJsonParseError::NoError) { - emitRenameWarning(tr("\"%1\" is corrupted.\n%2") - .arg(sourceFileInfo.absoluteFilePath(), parseError.errorString())); - return; - } - - if (document.isObject()) { - QJsonObject rootObject = document.object(); - - bool collectionContainsOldName = rootObject.contains(oldName); - bool collectionContainsNewName = rootObject.contains(newName); - - if (!collectionContainsOldName) { - emitRenameWarning( - tr("The model group doesn't contain the old model name (%1).").arg(oldName)); - return; - } - - if (collectionContainsNewName) { - emitRenameWarning( - tr("The model name \"%1\" already exists in the model group.").arg(newName)); - return; - } - - QJsonValue oldValue = rootObject.value(oldName); - rootObject.insert(newName, oldValue); - rootObject.remove(oldName); - - document.setObject(rootObject); - - if (!CollectionEditorUtils::writeToJsonDocument(jsonPath, document)) { - emitRenameWarning(tr("Can't write to \"%1\".").arg(sourceFileInfo.absoluteFilePath())); - return; - } - - CollectionListModel *list = m_collectionList.at(nodeIndex.row()).data(); - bool updateSelectedNames = list && list == m_previousSelectedList.data(); - emit collectionRenamed(oldName, newName); - updateCollectionList(nodeIndex); - - if (updateSelectedNames) { - list = m_collectionList.at(nodeIndex.row()).data(); - if (m_selectedCollectionName == oldName) { - list->selectCollectionName(newName); - setSelectedCollectionName(newName); - } else { - // reselect to update ID if it's changed due to renaming and order changes - list->selectCollectionName(m_selectedCollectionName); - } - } - } -} - -void CollectionSourceModel::onCollectionsRemoved(CollectionListModel *collectionList, - const QStringList &removedCollections) -{ - using Utils::FilePath; - using Utils::FileReader; - auto emitDeleteWarning = [this](const QString &msg) -> void { - emit warning(tr("Delete Model"), msg); - }; - - const ModelNode node = collectionList->sourceNode(); - const QModelIndex nodeIndex = indexOfNode(node); - - if (!nodeIndex.isValid()) { - emitDeleteWarning(tr("Invalid node")); - return; - } - - if (node.type() != CollectionEditorConstants::JSONCOLLECTIONMODEL_TYPENAME) { - emitDeleteWarning(tr("Invalid node type")); - return; - } - - QString sourceFileAddress = CollectionEditorUtils::getSourceCollectionPath(node); - - QFileInfo sourceFileInfo(sourceFileAddress); - if (!sourceFileInfo.isFile()) { - emitDeleteWarning(tr("The selected node has an invalid source address")); - return; - } - - FilePath jsonPath = FilePath::fromUserInput(sourceFileAddress); - FileReader jsonFile; - if (!jsonFile.fetch(jsonPath)) { - emitDeleteWarning(tr("Can't read or write \"%1\".\n%2") - .arg(sourceFileInfo.absoluteFilePath(), jsonFile.errorString())); - return; - } - - QJsonParseError parseError; - QJsonDocument document = QJsonDocument::fromJson(jsonFile.data(), &parseError); - if (parseError.error != QJsonParseError::NoError) { - emitDeleteWarning(tr("\"%1\" is corrupted.\n%2") - .arg(sourceFileInfo.absoluteFilePath(), parseError.errorString())); - return; - } - - if (document.isObject()) { - QJsonObject rootObject = document.object(); - - QStringList collectionsRemovedFromDocument; - for (const QString &collectionName : removedCollections) { - bool sourceContainsCollection = rootObject.contains(collectionName); - if (sourceContainsCollection) { - rootObject.remove(collectionName); - collectionsRemovedFromDocument << collectionName; - } else { - emitDeleteWarning(tr("The model group doesn't contain the model name (%1).") - .arg(sourceContainsCollection)); - } - } - - document.setObject(rootObject); - - if (!CollectionEditorUtils::writeToJsonDocument(jsonPath, document)) { - emitDeleteWarning(tr("Can't write to \"%1\".").arg(sourceFileInfo.absoluteFilePath())); - return; - } - - for (const QString &collectionName : std::as_const(collectionsRemovedFromDocument)) - emit collectionRemoved(collectionName); - - updateCollectionList(nodeIndex); - if (m_previousSelectedList == collectionList) - onSelectedCollectionChanged(collectionList, collectionList->selectedIndex()); - } -} - -void CollectionSourceModel::setSelectedIndex(int idx) -{ - idx = (idx > -1 && idx < m_collectionSources.count()) ? idx : -1; - - if (m_selectedIndex != idx) { - QModelIndex previousIndex = index(m_selectedIndex); - QModelIndex newIndex = index(idx); - - m_selectedIndex = idx; - - if (previousIndex.isValid()) - emit dataChanged(previousIndex, previousIndex, {SelectedRole}); - - if (newIndex.isValid()) - emit dataChanged(newIndex, newIndex, {SelectedRole}); - - emit selectedIndexChanged(idx); - - if (idx > -1) { - QPointer relatedCollectionList = m_collectionList.at(idx).data(); - if (relatedCollectionList) { - if (relatedCollectionList->selectedIndex() < 0) - relatedCollectionList->selectCollectionIndex(0, true); - } else if (m_previousSelectedList) { - m_previousSelectedList->selectCollectionIndex(-1); - m_previousSelectedList = {}; - setSelectedCollectionName({}); - } - } - } -} - -void CollectionSourceModel::setSelectedCollectionName(const QString &collectionName) -{ - if (m_selectedCollectionName != collectionName) { - m_selectedCollectionName = collectionName; - emit collectionSelected(m_selectedCollectionName); - } -} - -void CollectionSourceModel::updateEmpty() -{ - bool isEmptyNow = m_collectionSources.isEmpty(); - if (m_isEmpty != isEmptyNow) { - m_isEmpty = isEmptyNow; - emit isEmptyChanged(m_isEmpty); - - if (m_isEmpty) - setSelectedIndex(-1); - } -} - -void CollectionSourceModel::updateCollectionList(QModelIndex index) -{ - if (!index.isValid()) - return; - - ModelNode sourceNode = sourceNodeAt(index.row()); - QSharedPointer oldList = m_collectionList.at(index.row()); - QSharedPointer newList = loadCollection(sourceNode, oldList); - if (oldList != newList) { - m_collectionList.replace(index.row(), newList); - emit dataChanged(index, index, {CollectionsRole}); - registerCollectionList(newList); - } -} - -void CollectionSourceModel::registerCollectionList( - const QSharedPointer &sharedCollectionList) -{ - CollectionListModel *collectionList = sharedCollectionList.data(); - if (collectionList == nullptr) - return; - - if (!collectionList->property("_is_registered_in_sourceModel").toBool()) { - collectionList->setProperty("_is_registered_in_sourceModel", true); - - connect(collectionList, - &CollectionListModel::selectedIndexChanged, - this, - [this, collectionList](int idx) { onSelectedCollectionChanged(collectionList, idx); }); - - connect(collectionList, - &CollectionListModel::collectionNameChanged, - this, - [this, collectionList](const QString &oldName, const QString &newName) { - onCollectionNameChanged(collectionList, oldName, newName); - }); - - connect(collectionList, - &CollectionListModel::collectionsRemoved, - this, - [this, collectionList](const QStringList &removedCollections) { - onCollectionsRemoved(collectionList, removedCollections); - }); - - connect(collectionList, &CollectionListModel::modelReset, this, [this, collectionList]() { - emit collectionNamesInitialized(collectionList->collections()); - }); - } - - if (collectionList->sourceNode().isValid()) - emit collectionNamesInitialized(collectionList->collections()); -} - -QModelIndex CollectionSourceModel::indexOfNode(const ModelNode &node) const -{ - return index(m_sourceIndexHash.value(node.internalId(), -1)); -} - -void CollectionJsonSourceFilterModel::registerDeclarativeType() -{ - qmlRegisterType("CollectionEditor", - 1, - 0, - "CollectionJsonSourceFilterModel"); -} - -bool CollectionJsonSourceFilterModel::filterAcceptsRow(int source_row, const QModelIndex &) const -{ - if (!sourceModel()) - return false; - QModelIndex sourceItem = sourceModel()->index(source_row, 0, {}); - return sourceItem.data(CollectionSourceModel::Roles::CollectionTypeRole).toString() == "json"; -} - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.h b/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.h deleted file mode 100644 index f988935c988..00000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionsourcemodel.h +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 - -#pragma once - -#include "modelnode.h" - -#include -#include -#include - -namespace QmlDesigner { - -class CollectionJsonSourceFilterModel; -class CollectionListModel; - -class CollectionSourceModel : public QAbstractListModel -{ - Q_OBJECT - - Q_PROPERTY(int selectedIndex MEMBER m_selectedIndex NOTIFY selectedIndexChanged) - Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged) - -public: - enum Roles { - NameRole = Qt::UserRole + 1, - NodeRole, - CollectionTypeRole, - SourceRole, - SelectedRole, - CollectionsRole - }; - - explicit CollectionSourceModel(QObject *parent = nullptr); - - virtual int rowCount(const QModelIndex &parent = QModelIndex()) const override; - virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; - virtual bool setData(const QModelIndex &index, - const QVariant &value, - int role = Qt::EditRole) override; - - Q_INVOKABLE virtual bool removeRows(int row, - int count = 1, - const QModelIndex &parent = QModelIndex()) override; - - virtual QHash roleNames() const override; - - void setSource(const ModelNode &source); - void reset(); - int sourceIndex(const ModelNode &node) const; - void addSource(const ModelNode &node); - void selectSource(const ModelNode &node); - - bool addCollectionToSource(const ModelNode &node, - const QString &collectionName, - const QJsonObject &newCollection, - QString *errorString = nullptr); - - ModelNode sourceNodeAt(int idx); - CollectionListModel *selectedCollectionList(); - - void updateNodeName(const ModelNode &node); - void updateNodeSource(const ModelNode &node); - - Q_INVOKABLE void selectSourceIndex(int idx, bool selectAtLeastOne = false); - Q_INVOKABLE void selectCollection(const QVariant &node, const QString &collectionName); - Q_INVOKABLE void deselect(); - Q_INVOKABLE void updateSelectedSource(bool selectAtLeastOne = false); - Q_INVOKABLE QString getUniqueCollectionName(const QString &baseName = {}) const; - Q_INVOKABLE bool collectionExists(const QString &collectionName) const; - -signals: - void selectedIndexChanged(int idx); - void collectionSelected(const QString &collectionName); - void collectionNamesInitialized(const QStringList &initialList); - void collectionRenamed(const QString &oldname, const QString &newName); - void collectionRemoved(const QString &collectionName); - - void isEmptyChanged(bool); - void warning(const QString &title, const QString &body); - -private slots: - void onSelectedCollectionChanged(CollectionListModel *collectionList, int collectionIndex); - void onCollectionNameChanged(CollectionListModel *collectionList, const QString &oldName, - const QString &newName); - void onCollectionsRemoved(CollectionListModel *collectionList, - const QStringList &removedCollections); - -private: - void setSelectedIndex(int idx); - void setSelectedCollectionName(const QString &collectionName); - void updateEmpty(); - void updateCollectionList(QModelIndex index); - void registerCollectionList(const QSharedPointer &collectionList); - QModelIndex indexOfNode(const ModelNode &node) const; - - using Super = QAbstractListModel; - - ModelNodes m_collectionSources; - QHash m_sourceIndexHash; // internalId -> index - QList> m_collectionList; - QPointer m_previousSelectedList; - QString m_selectedCollectionName; - int m_selectedIndex = -1; - bool m_isEmpty = true; -}; - -class CollectionJsonSourceFilterModel : public QSortFilterProxyModel -{ - Q_OBJECT - -public: - static void registerDeclarativeType(); - -protected: - bool filterAcceptsRow(int source_row, const QModelIndex &) const override; -}; - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp index 442a79cbd5a..723d0beca88 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp @@ -6,7 +6,7 @@ #include "collectiondetailsmodel.h" #include "collectioneditorconstants.h" #include "collectioneditorutils.h" -#include "collectionsourcemodel.h" +#include "collectionlistmodel.h" #include "collectionwidget.h" #include "datastoremodelnode.h" #include "designmodecontext.h" @@ -81,29 +81,33 @@ QmlDesigner::WidgetInfo CollectionView::widgetInfo() auto collectionEditorContext = new Internal::CollectionEditorContext(m_widget.data()); Core::ICore::addContextObject(collectionEditorContext); - CollectionSourceModel *sourceModel = m_widget->sourceModel().data(); + CollectionListModel *listModel = m_widget->listModel().data(); - connect(sourceModel, - &CollectionSourceModel::collectionSelected, + connect(listModel, + &CollectionListModel::selectedCollectionNameChanged, this, [this](const QString &collection) { m_widget->collectionDetailsModel()->loadCollection(dataStoreNode(), collection); }); - connect(sourceModel, &CollectionSourceModel::isEmptyChanged, this, [this](bool isEmpty) { + connect(listModel, &CollectionListModel::isEmptyChanged, this, [this](bool isEmpty) { if (isEmpty) m_widget->collectionDetailsModel()->loadCollection({}, {}); }); - connect(sourceModel, - &CollectionSourceModel::collectionNamesInitialized, - this, - [this](const QStringList &collectionNames) { - m_dataStore->setCollectionNames(collectionNames); - }); + connect(listModel, &CollectionListModel::modelReset, this, [this] { + CollectionListModel *listModel = m_widget->listModel().data(); + if (listModel->sourceNode() == m_dataStore->modelNode()) + m_dataStore->setCollectionNames(listModel->collections()); + }); - connect(sourceModel, - &CollectionSourceModel::collectionRenamed, + connect(listModel, + &CollectionListModel::collectionAdded, + this, + [this](const QString &collectionName) { m_dataStore->addCollection(collectionName); }); + + connect(listModel, + &CollectionListModel::collectionNameChanged, this, [this](const QString &oldName, const QString &newName) { m_dataStore->renameCollection(oldName, newName); @@ -112,13 +116,15 @@ QmlDesigner::WidgetInfo CollectionView::widgetInfo() newName); }); - connect(sourceModel, - &CollectionSourceModel::collectionRemoved, + connect(listModel, + &CollectionListModel::collectionsRemoved, this, - [this](const QString &collectionName) { - m_dataStore->removeCollection(collectionName); - m_widget->collectionDetailsModel()->removeCollection(dataStoreNode(), - collectionName); + [this](const QStringList &collectionNames) { + m_dataStore->removeCollections(collectionNames); + for (const QString &collectionName : collectionNames) { + m_widget->collectionDetailsModel()->removeCollection(dataStoreNode(), + collectionName); + } }); } @@ -144,29 +150,7 @@ void CollectionView::modelAboutToBeDetached([[maybe_unused]] Model *model) m_dataStoreTypeFound = false; disconnect(m_documentUpdateConnection); QTC_ASSERT(m_delayedTasks.isEmpty(), m_delayedTasks.clear()); - m_widget->sourceModel()->reset(); -} - -void CollectionView::nodeRemoved(const ModelNode &removedNode, - [[maybe_unused]] const NodeAbstractProperty &parentProperty, - [[maybe_unused]] PropertyChangeFlags propertyChange) -{ - if (isStudioCollectionModel(removedNode)) - m_widget->sourceModel()->updateSelectedSource(true); -} - -void CollectionView::variantPropertiesChanged(const QList &propertyList, - [[maybe_unused]] PropertyChangeFlags propertyChange) -{ - for (const VariantProperty &property : propertyList) { - ModelNode node(property.parentModelNode()); - if (isStudioCollectionModel(node)) { - if (property.name() == "objectName") - m_widget->sourceModel()->updateNodeName(node); - else if (property.name() == CollectionEditorConstants::SOURCEFILE_PROPERTY) - m_widget->sourceModel()->updateNodeSource(node); - } - } + m_widget->listModel()->setDataStoreNode(); } void CollectionView::selectedNodesChanged(const QList &selectedNodeList, @@ -190,11 +174,6 @@ void CollectionView::selectedNodesChanged(const QList &selectedNodeLi // More than one model is selected. So ignore them if (selectedCollectionNodes.size() > 1) return; - - if (selectedCollectionNodes.size() == 1) { // If exactly one model is selected - m_widget->sourceModel()->selectSource(selectedCollectionNodes.first()); - return; - } } void CollectionView::customNotification(const AbstractView *, @@ -298,7 +277,7 @@ void CollectionView::assignCollectionToSelectedNode(const QString &collectionNam void CollectionView::addNewCollection(const QString &collectionName, const QJsonObject &localCollection) { addTask(QSharedPointer( - new AddCollectionTask(this, m_widget->sourceModel(), localCollection, collectionName))); + new AddCollectionTask(this, m_widget->listModel(), localCollection, collectionName))); } void CollectionView::openCollection(const QString &collectionName) @@ -309,7 +288,6 @@ void CollectionView::openCollection(const QString &collectionName) void CollectionView::registerDeclarativeType() { CollectionDetails::registerDeclarativeType(); - CollectionJsonSourceFilterModel::registerDeclarativeType(); } void CollectionView::resetDataStoreNode() @@ -317,7 +295,7 @@ void CollectionView::resetDataStoreNode() m_dataStore->reloadModel(); ModelNode dataStore = m_dataStore->modelNode(); - if (!dataStore || m_widget->sourceModel()->sourceIndex(dataStore) > -1) + if (!dataStore || m_widget->listModel()->sourceNode() == dataStore) return; bool dataStoreSingletonFound = m_dataStoreTypeFound; @@ -336,7 +314,7 @@ void CollectionView::resetDataStoreNode() } if (dataStoreSingletonFound) { - m_widget->sourceModel()->setSource(dataStore); + m_widget->listModel()->setDataStoreNode(dataStore); m_dataStoreTypeFound = true; while (!m_delayedTasks.isEmpty()) @@ -415,7 +393,7 @@ void CollectionView::onItemLibraryNodeCreated(const ModelNode &node) { if (node.metaInfo().isQtQuickListView()) { addTask(QSharedPointer( - new DropListViewTask(this, m_widget->sourceModel(), node))); + new DropListViewTask(this, m_widget->listModel(), node))); } } @@ -439,61 +417,49 @@ void CollectionView::addTask(QSharedPointer task) m_delayedTasks << task; } -CollectionTask::CollectionTask(CollectionView *view, CollectionSourceModel *sourceModel) +CollectionTask::CollectionTask(CollectionView *view, CollectionListModel *listModel) : m_collectionView(view) - , m_sourceModel(sourceModel) + , m_listModel(listModel) {} DropListViewTask::DropListViewTask(CollectionView *view, - CollectionSourceModel *sourceModel, + CollectionListModel *listModel, const ModelNode &node) - : CollectionTask(view, sourceModel) + : CollectionTask(view, listModel) , m_node(node) {} void DropListViewTask::process() { AbstractView *view = m_node.view(); - if (!m_node || !m_collectionView || !m_sourceModel || !view) + if (!m_node || !m_collectionView || !m_listModel || !view) return; - const QString newCollectionName = m_sourceModel->getUniqueCollectionName("ListModel"); - m_sourceModel->addCollectionToSource(m_collectionView->dataStoreNode(), - newCollectionName, - CollectionEditorUtils::defaultColorCollection()); + const QString newCollectionName = m_listModel->getUniqueCollectionName("ListModel"); + m_listModel->addCollection(newCollectionName, CollectionEditorUtils::defaultColorCollection()); m_collectionView->openCollection(newCollectionName); m_collectionView->assignCollectionToNode(newCollectionName, m_node); } AddCollectionTask::AddCollectionTask(CollectionView *view, - CollectionSourceModel *sourceModel, + CollectionListModel *listModel, const QJsonObject &localJsonObject, const QString &collectionName) - : CollectionTask(view, sourceModel) + : CollectionTask(view, listModel) , m_localJsonObject(localJsonObject) , m_name(collectionName) {} void AddCollectionTask::process() { - if (!m_sourceModel) + if (!m_listModel) return; - QString errorMsg; - - const QString newCollectionName = m_sourceModel->collectionExists(m_name) - ? m_sourceModel->getUniqueCollectionName(m_name) + const QString newCollectionName = m_listModel->collectionExists(m_name) + ? m_listModel->getUniqueCollectionName(m_name) : m_name; - bool added = m_sourceModel->addCollectionToSource(m_collectionView->dataStoreNode(), - newCollectionName, - m_localJsonObject, - &errorMsg); - - if (!added) { - emit m_sourceModel->warning(m_sourceModel->tr("Can not add a model to the JSON file"), - errorMsg); - } + m_listModel->addCollection(newCollectionName, m_localJsonObject); } } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionview.h b/src/plugins/qmldesigner/components/collectioneditor/collectionview.h index 5606ddc6b48..a4b16c4c276 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionview.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionview.h @@ -16,7 +16,7 @@ class Document; namespace QmlDesigner { class CollectionDetails; -class CollectionSourceModel; +class CollectionListModel; class CollectionTask; class CollectionWidget; class DataStoreModelNode; @@ -34,13 +34,6 @@ public: void modelAttached(Model *model) override; void modelAboutToBeDetached(Model *model) override; - void nodeRemoved(const ModelNode &removedNode, - const NodeAbstractProperty &parentProperty, - PropertyChangeFlags propertyChange) override; - - void variantPropertiesChanged(const QList &propertyList, - PropertyChangeFlags propertyChange) override; - void selectedNodesChanged(const QList &selectedNodeList, const QList &lastSelectedNodeList) override; @@ -87,7 +80,7 @@ private: class CollectionTask { public: - CollectionTask(CollectionView *view, CollectionSourceModel *sourceModel); + CollectionTask(CollectionView *view, CollectionListModel *listModel); CollectionTask() = delete; virtual ~CollectionTask() = default; @@ -95,13 +88,13 @@ public: protected: QPointer m_collectionView; - QPointer m_sourceModel; + QPointer m_listModel; }; class DropListViewTask : public CollectionTask { public: - DropListViewTask(CollectionView *view, CollectionSourceModel *sourceModel, const ModelNode &node); + DropListViewTask(CollectionView *view, CollectionListModel *listModel, const ModelNode &node); void process() override; @@ -113,7 +106,7 @@ class AddCollectionTask : public CollectionTask { public: AddCollectionTask(CollectionView *view, - CollectionSourceModel *sourceModel, + CollectionListModel *listModel, const QJsonObject &localJsonObject, const QString &collectionName); diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp index 3e1b2e0129c..093729dc67b 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp @@ -7,7 +7,7 @@ #include "collectiondetailsmodel.h" #include "collectiondetailssortfiltermodel.h" #include "collectioneditorutils.h" -#include "collectionsourcemodel.h" +#include "collectionlistmodel.h" #include "collectionview.h" #include "designmodewidget.h" #include "qmldesignerconstants.h" @@ -56,7 +56,7 @@ namespace QmlDesigner { CollectionWidget::CollectionWidget(CollectionView *view) : QFrame() , m_view(view) - , m_sourceModel(new CollectionSourceModel) + , m_listModel(new CollectionListModel) , m_collectionDetailsModel(new CollectionDetailsModel) , m_collectionDetailsSortFilterModel(std::make_unique()) , m_quickWidget(new StudioQuickWidget(this)) @@ -69,7 +69,7 @@ CollectionWidget::CollectionWidget(CollectionView *view) icontext->setContext(context); icontext->setWidget(this); - connect(m_sourceModel, &CollectionSourceModel::warning, this, &CollectionWidget::warn); + connect(m_listModel, &CollectionListModel::warning, this, &CollectionWidget::warn); m_collectionDetailsSortFilterModel->setSourceModel(m_collectionDetailsModel); @@ -90,7 +90,7 @@ CollectionWidget::CollectionWidget(CollectionView *view) auto map = m_quickWidget->registerPropertyMap("CollectionEditorBackend"); map->setProperties({ {"rootView", QVariant::fromValue(this)}, - {"model", QVariant::fromValue(m_sourceModel.data())}, + {"model", QVariant::fromValue(m_listModel.data())}, {"collectionDetailsModel", QVariant::fromValue(m_collectionDetailsModel.data())}, {"collectionDetailsSortFilterModel", QVariant::fromValue(m_collectionDetailsSortFilterModel.get())}, @@ -112,9 +112,9 @@ void CollectionWidget::contextHelp(const Core::IContext::HelpCallback &callback) callback({}); } -QPointer CollectionWidget::sourceModel() const +QPointer CollectionWidget::listModel() const { - return m_sourceModel; + return m_listModel; } QPointer CollectionWidget::collectionDetailsModel() const @@ -262,7 +262,7 @@ void CollectionWidget::assignCollectionToSelectedNode(const QString collectionNa void CollectionWidget::openCollection(const QString &collectionName) { - m_sourceModel->selectCollection(QVariant::fromValue(m_view->dataStoreNode()), collectionName); + m_listModel->selectCollectionName(collectionName); QmlDesignerPlugin::instance()->mainWidget()->showDockWidget("CollectionEditor", true); } diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h index a3218bd1bca..0957bd81e0f 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h @@ -13,7 +13,7 @@ namespace QmlDesigner { class CollectionDetailsModel; class CollectionDetailsSortFilterModel; -class CollectionSourceModel; +class CollectionListModel; class CollectionView; class ModelNode; @@ -27,7 +27,7 @@ public: CollectionWidget(CollectionView *view); void contextHelp(const Core::IContext::HelpCallback &callback) const; - QPointer sourceModel() const; + QPointer listModel() const; QPointer collectionDetailsModel() const; void reloadQmlSource(); @@ -61,7 +61,7 @@ private: QString generateUniqueCollectionName(const ModelNode &node, const QString &name); QPointer m_view; - QPointer m_sourceModel; + QPointer m_listModel; QPointer m_collectionDetailsModel; std::unique_ptr m_collectionDetailsSortFilterModel; QScopedPointer m_quickWidget; diff --git a/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.cpp b/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.cpp index 77122526555..503b3f1ea31 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.cpp @@ -390,6 +390,14 @@ void DataStoreModelNode::setCollectionNames(const QStringList &newCollectionName update(); } +void DataStoreModelNode::addCollection(const QString &collectionName) +{ + if (!m_collectionPropertyNames.contains(collectionName)) { + m_collectionPropertyNames.insert(collectionName, {}); + update(); + } +} + void DataStoreModelNode::renameCollection(const QString &oldName, const QString &newName) { ModelNode dataStoreNode = modelNode(); @@ -420,12 +428,18 @@ void DataStoreModelNode::renameCollection(const QString &oldName, const QString << QString("There is no old collection name registered with this name \"%1\"").arg(oldName); } -void DataStoreModelNode::removeCollection(const QString &collectionName) +void DataStoreModelNode::removeCollections(const QStringList &collectionNames) { - if (m_collectionPropertyNames.contains(collectionName)) { - m_collectionPropertyNames.remove(collectionName); - update(); + bool updateRequired = false; + for (const QString &collectionName : collectionNames) { + if (m_collectionPropertyNames.contains(collectionName)) { + m_collectionPropertyNames.remove(collectionName); + updateRequired = true; + } } + + if (updateRequired) + update(); } void DataStoreModelNode::assignCollectionToNode(AbstractView *view, diff --git a/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.h b/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.h index d23908bc0c5..6cd969edbe6 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.h +++ b/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.h @@ -31,8 +31,9 @@ public: ModelNode modelNode() const; void setCollectionNames(const QStringList &newCollectionNames); + void addCollection(const QString &collectionName); void renameCollection(const QString &oldName, const QString &newName); - void removeCollection(const QString &collectionName); + void removeCollections(const QStringList &collectionNames); void assignCollectionToNode(AbstractView *view, const ModelNode &targetNode, From 555772130ccee80495adee96c06736b2a03a418a Mon Sep 17 00:00:00 2001 From: Pranta Dastider Date: Thu, 29 Feb 2024 18:23:53 +0100 Subject: [PATCH 120/176] QmlDesigner: Update instructions in the QML Modules with Plugins doc This patch updates instructions in the QML Modules with Plugins document. It also includes a new image to give a visual idea about the instruction. Fixes: QDS-12065 Change-Id: I35f0c7f96196c772cbcdd728817ea409318ff952 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mats Honkamaa Reviewed-by: Johanna Vanhatapio --- .../qtquick/qtquick-modules-with-plugins.qdoc | 19 +++--------------- .../doc/images/studio-qt-development-kit.webp | Bin 0 -> 2460 bytes 2 files changed, 3 insertions(+), 16 deletions(-) create mode 100644 doc/qtdesignstudio/examples/doc/images/studio-qt-development-kit.webp diff --git a/doc/qtcreator/src/qtquick/qtquick-modules-with-plugins.qdoc b/doc/qtcreator/src/qtquick/qtquick-modules-with-plugins.qdoc index 281c17319c6..97cf62bc015 100644 --- a/doc/qtcreator/src/qtquick/qtquick-modules-with-plugins.qdoc +++ b/doc/qtcreator/src/qtquick/qtquick-modules-with-plugins.qdoc @@ -120,27 +120,14 @@ the emulation layer must be built with the same Qt version and compiler as the QML modules. - On Windows, select \uicontrol Help > \uicontrol {About Qt Design Studio} to - check the Qt version and compiler that you need to use to build your plugin. - For example: \c {Based on Qt 5.15.2 (MSVC 2019, 64 bit)}. - - On macOS, select \uicontrol {Qt Design Studio} > - \uicontrol {About Qt Design Studio} to see something like: - \c {Based on Qt 5.15.2 (Clang 10.0 (Apple), 64 bit)}. + In \QDS, find the Qt version in the bottom toolbar next to + \inlineimage icons/settings.png. + \image studio-qt-development-kit.webp A plugin should behave differently depending on whether it is run by the emulation layer or an application. For example, animations should not be run in the \uicontrol Design mode. You can use the value of the \c QML_PUPPET_MODE environment variable to check whether the plugin is currently being run by an application or edited in the \uicontrol Design mode. - - If you want to use a different module in the \uicontrol Design mode - than in your actual application for example to mockup C++ items, - you can use \c{QML_DESIGNER_IMPORT_PATH} - in the \c{.pro} file (for qmake projects), or declare and set the property - \c qmlDesignerImportPaths in your product (for Qbs projects). - Modules in the import paths defined in \c{QML_DESIGNER_IMPORT_PATH} will be - used only in the \uicontrol Design mode. - For an example, see \l {Qt Quick Controls - Contact List}. \endif */ diff --git a/doc/qtdesignstudio/examples/doc/images/studio-qt-development-kit.webp b/doc/qtdesignstudio/examples/doc/images/studio-qt-development-kit.webp new file mode 100644 index 0000000000000000000000000000000000000000..ab791bfc0b676901601828e130e2e0688c989851 GIT binary patch literal 2460 zcmWIYbaR`+$-ofq>J$(bU=hK^z`(%4z`!8L$e_T$5EfwJ^NgK=L0^!OgTeIT*?;vD z?`-)r|K`n`GgB?@8p-RNd2`T6?*5@b#-bx<1PqVLye>R4Gv?yOL-j|44D}v(1+q_< zRu+BGVOh#5zFQ#uc7uGYUJG}@iZctf407c8*7MrtH79*vz;~u>NzBg0`P`H8Zb#jI zRy%)2GZt{~ZpvQ}bf-wvzW#^(%H*|4H1xSDj(kx!0n?Ry%Ia?|mZGB;~-mdCH8;tE=ob z&Yt$#BeR_0!1*qZ?Y@cOzJ7OFIiCsYFnF(Onf%*otw{I0j=7czp-oQr_C;@JFpr*j zWSyef_62*29!;5N^|e8wZ~bur*WOuS^0_6aEVr_o&tAM!WQw=&vBqm&0Vjh4bgpIG zoWJ17M)Qr|AJ$*puf0mbgHcO#?rMM9vf=~Huo*(RABw^i`As}eKP05 zM#U<1}>{ZPVr_}w^PY+*lcZ=@1;C;q&%bjC843e&DWoa8WF? zz%K{3PfQ7Cnft{`S5|zH=Hsu{oQWy6OjGMm;;_nv8# z5K^}4(L`HGZD+Qv2_l+%jg*=~%hP7toqZxE*t9i%w$a?#%p7mTC2eaM4|G-t@cc;G zDtfr+z$~5a7@5~+UQShaT@@5{MVG;0qs8RoPV4;}%FfKXx|=nsd+7(|oHIo{$JvFi zW~4IklnSINA7U^&wDMMRV8F)@zss)Y%rE|alY8}+@Xzu0PTN#+Ur0VV-8kd9+;i5# z)5Q@i1eFulLEPdzgOR`*- zedMZ<=l^3@&oAJ>{7@;cRiEibSL@>oW&RWPUE*p`k2<%$^uQveNp4B@%X9ayh-L`z zD^t9d`r9GGp*pQHyCkciXiLWF$(4bIR=l;sJriQ4NOLSU3~Ic1>O-=H;hw6RNtb3m zx%r^;{fbWyco#0R`E9U5!_oqwZ)LFugmt|yP4i%Q;A6I7zV)tY)=_=k(QeN! zm~8g)u2{Y{n&DTPNwDF1?{kY}FPc7%Qd8@-i2G42V!Fe!m;KTWRSpH`*Gr;Sa!g3l znj9s-BJgV>&qeMlc}joxe@_umm8)6k^IE*Y;KydpvLEUGTh5$T6l7pn5V7URM(v%} zg>$C8KGEzxRrPNcZ~2}--GUb9QkZTRm%It_)~Yb&$k;jesjU^0z-~|7?DCvshPJA( z+?6|4Ui(?k^=-|u1-Z|j{#&zU*Dlr{hBMAZT~>3Ry(s(m)tGsUHCtI3Ev3Apb|3h) zVzm3$tJuM&Hs2;g- z>K3gxKJria&xq93ciCz8t_xln5ws*t%#~|@&v~uQj2j+WpLzD&YK>S}51ZBMYli#F z)g;2AYk3TgoM?ZxK`wg2oU=VEiVe0dW>H&n=9NgnvC~TPe;EE?to!_El4wNqv506j zOJ>&N?81AGd+mN5fA4Ilz%%7T46?7jeG=V$tk&wt%%AsPYp?j*V6uCYos?gY#p3Mk zZLd=ARUdn5Qm|Qg^+kb(p2&uktP7bJyjwfb?c`yBr^QpR%#u>S=pA|G_tPn4Dywtm5Ldj~jXlkK&9(g&)`cvds`Hd5RQiidb>X&{ozhQ*8wC&V ze0P~+A>)C2dGjafW+!`aZ}vQTZ{Gv=rPIG~?N8*Ox&H z+zeo66k=dzU|?WmU}Rumlwx27vl$qY7^UItEJh8e8YTt?MneWB1_lOs1_p*AMk6p= zgn@x!VtXb73smn41_p*J41CKN7#P$AV623U@Vfq{X^{sWi|*2CfqX7dIlrG_&KFt9R$LXiOg{+a

    Date: Fri, 1 Mar 2024 13:53:23 +0100 Subject: [PATCH 121/176] QmlDesigner: Fix importmodule.qmldir.tpl Module -> module Change-Id: Ib441f6aa2fb3b9c4d7d69654acce8db83c4f93fe Reviewed-by: Ali Kianian Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen Reviewed-by: --- .../projects/shared-plugin/name/importmodule.qmldir.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/shared-plugin/name/importmodule.qmldir.tpl b/share/qtcreator/qmldesigner/studio_templates/projects/shared-plugin/name/importmodule.qmldir.tpl index c0050290386..be4e251f8c2 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/shared-plugin/name/importmodule.qmldir.tpl +++ b/share/qtcreator/qmldesigner/studio_templates/projects/shared-plugin/name/importmodule.qmldir.tpl @@ -1,4 +1,4 @@ -Module %{ImportModuleName} +module %{ImportModuleName} singleton Constants 1.0 Constants.qml EventListSimulator 1.0 EventListSimulator.qml EventListModel 1.0 EventListModel.qml From 0db0e0659260dafa548ebf14fb4c1fb24347b253 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Mon, 4 Mar 2024 15:24:30 +0200 Subject: [PATCH 122/176] QmlDesigner: Fix effect composer doesn't open by double click Fixes: QDS-12158 Change-Id: Ie3512cd933438806cbf7dd7f44ca4dab2546df3d Reviewed-by: Miikka Heikkinen --- .../components/componentcore/modelnodeoperations.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp index 98e0e4aef59..ca7bd21689d 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp @@ -1662,7 +1662,7 @@ void openEffectComposer(const QString &filePath) if (ModelNodeOperations::isEffectComposerActivated()) { QmlDesignerPlugin::instance()->viewManager() .emitCustomNotification("open_effectcomposer_composition", {}, {filePath}); - QmlDesignerPlugin::instance()->mainWidget()->showDockWidget("Effect Composer", true); + QmlDesignerPlugin::instance()->mainWidget()->showDockWidget("EffectComposer", true); } else { ModelNodeOperations::openOldEffectMaker(filePath); } From 88c52f0c253f162dca2cce082e1eb4018a1fb9a5 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Mon, 4 Mar 2024 14:43:10 +0200 Subject: [PATCH 123/176] QmlDesigner: Add CustomComboBox to StudioControls CustomComboBox requires a custom popup. This will be base for an upcoming multi-select HelperWidgets Combobox. Change-Id: I92869d064b69c11f577b297fc48a38f76c423060 Reviewed-by: Miikka Heikkinen --- .../imports/StudioControls/CustomComboBox.qml | 119 ++++++++++++++++++ .../imports/StudioControls/qmldir | 1 + 2 files changed, 120 insertions(+) create mode 100644 share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/CustomComboBox.qml diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/CustomComboBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/CustomComboBox.qml new file mode 100644 index 00000000000..64507bf5241 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/CustomComboBox.qml @@ -0,0 +1,119 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQuick +import HelperWidgets as HelperWidgets +import StudioControls as StudioControls +import StudioTheme as StudioTheme + +// A ComboBox with a custom popup window +StudioControls.ComboBox { + id: root + + required property Component itemDelegate + required property var itemsModel + + required property Item mainRoot + required property var rootView + + readonly property int popupHeight: Math.min(800, col.height + 2) + + // hide default popup + popup.width: 0 + popup.height: 0 + + function itemAt(idx) { + return repeater.itemAt(idx) + } + + function calculateWindowGeometry() { + let globalPos = root.rootView.globalPos(mainRoot.mapFromItem(root, 0, 0)) + let screenRect = root.rootView.screenRect(); + + window.width = col.width + 2 // 2: scrollView left and right 1px margins + + let newX = globalPos.x + root.width - window.width + if (newX < screenRect.x) + newX = globalPos.x + + let newY = Math.min(screenRect.y + screenRect.height, + Math.max(screenRect.y, globalPos.y + root.height - 1)) + + // Check if we have more space above or below the control, and put control on that side, + // unless we have enough room for maximum size popup under the control + let newHeight + let screenY = newY - screenRect.y + let availableHeight = screenRect.height - screenY + if (availableHeight > screenY || availableHeight > root.popupHeight) { + newHeight = Math.min(root.popupHeight, availableHeight) + } else { + newHeight = Math.min(root.popupHeight, screenY - root.height) + newY = newY - newHeight - root.height + 1 + } + + window.height = newHeight + window.x = newX + window.y = newY + } + + Connections { + target: root.popup + + function onAboutToShow() { + root.calculateWindowGeometry() + + window.show() + window.requestActivate() + + // Geometry can get corrupted by first show after screen change, so recalc it + root.calculateWindowGeometry() + } + + function onAboutToHide() { + window.hide() + } + } + + Window { + id: window + + flags: Qt.Dialog | Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint + + onActiveFocusItemChanged: { + if (!window.activeFocusItem && !root.hovered && root.popup.opened) + root.popup.close() + } + + Rectangle { + anchors.fill: parent + color: StudioTheme.Values.themePanelBackground + border.color: StudioTheme.Values.themeInteraction + border.width: 1 + focus: true + + HelperWidgets.ScrollView { + anchors.fill: parent + anchors.margins: 1 + + Column { + id: col + + padding: 5 + spacing: 2 + + Repeater { + id: repeater + + model: root.itemsModel + delegate: root.itemDelegate + } + } + } + + Keys.onPressed: function(event) { + if (event.key === Qt.Key_Escape && root.popup.opened) + root.popup.close() + } + } + } +} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/qmldir b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/qmldir index 5376aeb0df2..e93f6f59ded 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/qmldir +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/qmldir @@ -10,6 +10,7 @@ ColorEditorPopup 1.0 impl/ColorEditorPopup.qml ColorPalette 1.0 impl/ColorPalette.qml ColorPicker 1.0 impl/ColorPicker.qml ComboBox 1.0 ComboBox.qml +CustomComboBox 1.0 CustomComboBox.qml ComboBoxInput 1.0 ComboBoxInput.qml ContextMenu 1.0 ContextMenu.qml Dialog 1.0 Dialog.qml From 61038a42f152f38c921975de359c6048f61a1b0f Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Mon, 4 Mar 2024 15:13:55 +0100 Subject: [PATCH 124/176] QmlDesigner: Use simplifiedTypeName instead of qualfiedTypeName This reduced the number of corner cases. Change-Id: I12f6622577caf3b5e661cbe13479893d37f02dc3 Reviewed-by: Marco Bubke --- .../designercore/metainfo/nodemetainfo.cpp | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp index 6e5f3fda2ee..8ae3c42c137 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp @@ -3027,11 +3027,9 @@ bool NodeMetaInfo::isQmlComponent() const if (!isValid()) return false; - auto type = m_privateData->qualfiedTypeName(); + auto type = simplifiedTypeName(); - return type == "Component" || type == "Qt.Component" || type == "QtQuick.Component" - || type == "QtQml.Component" || type == ".QQmlComponent" || type == "QQmlComponent" - || type == "QML.Component" || type == "QtQml.Base.Component"; + return type == "Component" || type == "QQmlComponent"; } } @@ -3041,7 +3039,7 @@ bool NodeMetaInfo::isFont() const using namespace Storage::Info; return isValid() && isTypeId(m_typeId, m_projectStorage->commonTypeId()); } else { - return isValid() && m_privateData->qualfiedTypeName() == "font"; + return isValid() && simplifiedTypeName() == "font"; } } @@ -3054,9 +3052,9 @@ bool NodeMetaInfo::isColor() const if (!isValid()) return false; - auto type = m_privateData->qualfiedTypeName(); + auto type = simplifiedTypeName(); - return type == "QColor" || type == "color" || type == "QtQuick.color"; + return type == "QColor" || type == "color" || type == "color"; } } From 9517e82d86cbfaf323345e65c920717adc97c051 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Mon, 4 Mar 2024 15:12:34 +0100 Subject: [PATCH 125/176] QmlDesigner: Remove Core depedency from QmlDesignerUtils Change-Id: I8affc32f5a3231884314f2d3b4b13d0061a169dc Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Marco Bubke --- src/plugins/qmldesigner/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 5498097c1dd..b77ec91d8fd 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -28,7 +28,7 @@ add_feature_info("Model tracing" ${ENABLE_MODEL_TRACING} "") add_qtc_library(QmlDesignerUtils STATIC DEPENDS - Qt::Gui Utils Qt::QmlPrivate Core + Qt::Gui Utils Qt::QmlPrivate PUBLIC_INCLUDES ${CMAKE_CURRENT_LIST_DIR}/utils SOURCES_PREFIX ${CMAKE_CURRENT_LIST_DIR}/utils From bfb5a152c9011bc7507ef4e216f0874c88d669e9 Mon Sep 17 00:00:00 2001 From: Burak Hancerli Date: Fri, 1 Mar 2024 16:35:17 +0100 Subject: [PATCH 126/176] QmlDesigner: Unify Qrc and Qmlrc package builders This patch removes the redundant codes from qtquick-components and qmldesigner and merges them under qmldesigner. Task-number: QDS-11990 Change-Id: I52ba6754182410ac7fadb2e891539e42ef821edf Reviewed-by: Marco Bubke Reviewed-by: Qt CI Patch Build Bot --- src/plugins/qmldesigner/CMakeLists.txt | 3 +- .../componentcore/resourcegenerator.cpp | 272 ++++++++++ .../componentcore/resourcegenerator.h | 18 + src/plugins/qmldesigner/generateresource.cpp | 495 ------------------ src/plugins/qmldesigner/generateresource.h | 18 - src/plugins/qmldesigner/qmldesignerplugin.cpp | 4 +- 6 files changed, 293 insertions(+), 517 deletions(-) create mode 100644 src/plugins/qmldesigner/components/componentcore/resourcegenerator.cpp create mode 100644 src/plugins/qmldesigner/components/componentcore/resourcegenerator.h delete mode 100644 src/plugins/qmldesigner/generateresource.cpp delete mode 100644 src/plugins/qmldesigner/generateresource.h diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index b77ec91d8fd..bb274f7703a 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -510,7 +510,6 @@ add_qtc_plugin(QmlDesigner documentmanager.cpp documentmanager.h documentwarningwidget.cpp documentwarningwidget.h dynamiclicensecheck.h - generateresource.cpp generateresource.h openuiqmlfiledialog.cpp openuiqmlfiledialog.h puppetenvironmentbuilder.cpp puppetenvironmentbuilder.h qmldesigner_global.h @@ -619,6 +618,7 @@ extend_qtc_plugin(QmlDesigner propertycomponentgeneratorinterface.h qmldesignericonprovider.cpp qmldesignericonprovider.h qmleditormenu.cpp qmleditormenu.h + resourcegenerator.cpp resourcegenerator.h selectioncontext.cpp selectioncontext.h theme.cpp theme.h zoomaction.cpp zoomaction.h @@ -910,7 +910,6 @@ extend_qtc_plugin(QmlDesigner include/propertybinding.h include/qml3dnode.h include/qmlvisualnode.h - ) extend_qtc_plugin(QmlDesigner diff --git a/src/plugins/qmldesigner/components/componentcore/resourcegenerator.cpp b/src/plugins/qmldesigner/components/componentcore/resourcegenerator.cpp new file mode 100644 index 00000000000..7a48ed0652a --- /dev/null +++ b/src/plugins/qmldesigner/components/componentcore/resourcegenerator.cpp @@ -0,0 +1,272 @@ +// 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 + +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include + +using namespace Utils; + +namespace QmlDesigner::ResourceGenerator { + +void generateMenuEntry(QObject *parent) +{ + const Core::Context projectContext(QmlProjectManager::Constants::QML_PROJECT_ID); + // ToDo: move this to QtCreator and add tr to the string then + auto action = new QAction(QCoreApplication::translate("QmlDesigner::GenerateResource", + "Generate QRC Resource File..."), + parent); + action->setEnabled(ProjectExplorer::ProjectManager::startupProject() != nullptr); + // todo make it more intelligent when it gets enabled + QObject::connect(ProjectExplorer::ProjectManager::instance(), + &ProjectExplorer::ProjectManager::startupProjectChanged, + [action]() { + if (auto buildSystem = QmlProjectManager::QmlBuildSystem::getStartupBuildSystem()) + action->setEnabled(!buildSystem->qtForMCUs()); + }); + + Core::Command *cmd = Core::ActionManager::registerAction(action, "QmlProject.CreateResource"); + QObject::connect(action, &QAction::triggered, []() { + auto project = ProjectExplorer::ProjectManager::startupProject(); + QTC_ASSERT(project, return); + const FilePath projectPath = project->projectFilePath().parentDir(); + auto qrcFilePath = Core::DocumentManager::getSaveFileNameWithExtension( + QCoreApplication::translate("QmlDesigner::GenerateResource", "Save Project as QRC File"), + projectPath.pathAppended(project->displayName() + ".qrc"), + QCoreApplication::translate("QmlDesigner::GenerateResource", "QML Resource File (*.qrc)")); + + if (qrcFilePath.toString().isEmpty()) + return; + + createQrcFile(qrcFilePath); + + Core::AsynchronousMessageBox::information( + QCoreApplication::translate("QmlDesigner::GenerateResource", "Success"), + QCoreApplication::translate("QmlDesigner::GenerateResource", + "Successfully generated QRC resource file\n %1") + .arg(qrcFilePath.toString())); + }); + + // ToDo: move this to QtCreator and add tr to the string then + auto rccAction = new QAction(QCoreApplication::translate("QmlDesigner::GenerateResource", + "Generate Deployable Package..."), + parent); + rccAction->setEnabled(ProjectExplorer::ProjectManager::startupProject() != nullptr); + QObject::connect(ProjectExplorer::ProjectManager::instance(), + &ProjectExplorer::ProjectManager::startupProjectChanged, + [rccAction]() { + rccAction->setEnabled(ProjectExplorer::ProjectManager::startupProject()); + }); + + Core::Command *cmd2 = Core::ActionManager::registerAction(rccAction, + "QmlProject.CreateRCCResource"); + QObject::connect(rccAction, &QAction::triggered, []() { + auto project = ProjectExplorer::ProjectManager::startupProject(); + QTC_ASSERT(project, return); + const FilePath projectPath = project->projectFilePath().parentDir(); + const FilePath qmlrcFilePath = Core::DocumentManager::getSaveFileNameWithExtension( + QCoreApplication::translate("QmlDesigner::GenerateResource", "Save Project as Resource"), + projectPath.pathAppended(project->displayName() + ".qmlrc"), + "QML Resource File (*.qmlrc);;Resource File (*.rcc)"); + + if (qmlrcFilePath.toString().isEmpty()) + return; + + QProgressDialog progress; + progress.setLabelText( + QCoreApplication::translate("QmlDesigner::GenerateResource", + "Generating deployable package. Please wait...")); + progress.setRange(0, 0); + progress.setWindowModality(Qt::WindowModal); + progress.setWindowFlags(Qt::Dialog | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint); + progress.setCancelButton(nullptr); + progress.show(); + + QFuture future = QtConcurrent::run( + [qmlrcFilePath]() { return createQmlrcFile(qmlrcFilePath); }); + + while (!future.isFinished()) + QCoreApplication::processEvents(); + + progress.close(); + + if (future.isCanceled()) { + qDebug() << "Operation canceled by user"; + return; + } + + if (!future.result()) { + Core::MessageManager::writeDisrupting( + QCoreApplication::translate("QmlDesigner::GenerateResource", + "Failed to generate deployable package!")); + QMessageBox msgBox; + msgBox.setWindowTitle( + QCoreApplication::translate("QmlDesigner::GenerateResource", "Error")); + msgBox.setText(QCoreApplication::translate( + "QmlDesigner::GenerateResource", + "Failed to generate deployable package!\n\nPlease check " + "the output pane for more information.")); + msgBox.exec(); + return; + } + + QMessageBox msgBox; + msgBox.setWindowTitle(QCoreApplication::translate("QmlDesigner::GenerateResource", "Success")); + msgBox.setText(QCoreApplication::translate("QmlDesigner::GenerateResource", + "Successfully generated deployable package")); + msgBox.exec(); + }); + + Core::ActionContainer *exportMenu = Core::ActionManager::actionContainer( + QmlProjectManager::Constants::EXPORT_MENU); + exportMenu->addAction(cmd, QmlProjectManager::Constants::G_EXPORT_GENERATE); + exportMenu->addAction(cmd2, QmlProjectManager::Constants::G_EXPORT_GENERATE); +} + +bool skipSuffix(const QString &fileName) +{ + const QStringList suffixes = {".pri", + ".pro", + ".user", + ".qrc", + ".qds", + "CMakeLists.txt", + ".db", + ".tmp", + ".TMP", + ".metainfo", + ".qtds", + ".db-shm", + ".db-wal"}; + + for (const auto &suffix : suffixes) + if (fileName.endsWith(suffix)) + return true; + + return false; +} + +QStringList getProjectFileList() +{ + const ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject(); + const FilePaths paths = project->files(ProjectExplorer::Project::AllFiles); + + const QDir dir(project->projectFilePath().parentDir().toString()); + QStringList selectedFileList; + + for (const FilePath &path : paths) { + QString relativePath = dir.relativeFilePath(path.toString()); + if (!skipSuffix(relativePath)) + selectedFileList.append(relativePath); + } + + return selectedFileList; +} + +bool createQrcFile(const FilePath &qrcFilePath) +{ + QFile qrcFile(qrcFilePath.toString()); + + if (!qrcFile.open(QIODeviceBase::WriteOnly | QIODevice::Truncate)) + return false; + + QXmlStreamWriter writer(&qrcFile); + writer.setAutoFormatting(true); + writer.setAutoFormattingIndent(0); + + writer.writeStartElement("RCC"); + writer.writeStartElement("qresource"); + + for (const QString &fileName : getProjectFileList()) + writer.writeTextElement("file", fileName.trimmed()); + + writer.writeEndElement(); + writer.writeEndElement(); + qrcFile.close(); + + return true; +} + +bool createQmlrcFile(const FilePath &qmlrcFilePath) +{ + const FilePath tempQrcFile = qmlrcFilePath.parentDir().pathAppended("temp.qrc"); + if (!createQrcFile(tempQrcFile)) + return false; + + const ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject(); + const QtSupport::QtVersion *qtVersion = QtSupport::QtKitAspect::qtVersion( + project->activeTarget()->kit()); + const FilePath rccBinary = qtVersion->rccFilePath(); + + Utils::Process rccProcess; + rccProcess.setWorkingDirectory(project->projectDirectory()); + + const QStringList arguments = {"--binary", + "--output", + qmlrcFilePath.toString(), + tempQrcFile.toString()}; + + rccProcess.setCommand({rccBinary, arguments}); + rccProcess.start(); + if (!rccProcess.waitForStarted()) { + Core::MessageManager::writeDisrupting( + QCoreApplication::translate("QmlDesigner::GenerateResource", + "Unable to generate resource file: %1") + .arg(qmlrcFilePath.toString())); + return false; + } + + QByteArray stdOut; + QByteArray stdErr; + if (!rccProcess.readDataFromProcess(&stdOut, &stdErr)) { + rccProcess.stop(); + Core::MessageManager::writeDisrupting( + QCoreApplication::translate("QmlDesigner::GenerateResource", + "A timeout occurred running \"%1\".") + .arg(rccProcess.commandLine().toUserOutput())); + return false; + } + + if (!stdOut.trimmed().isEmpty()) + Core::MessageManager::writeFlashing(QString::fromLocal8Bit(stdOut)); + + if (!stdErr.trimmed().isEmpty()) + Core::MessageManager::writeFlashing(QString::fromLocal8Bit(stdErr)); + + if (rccProcess.exitStatus() != QProcess::NormalExit) { + Core::MessageManager::writeDisrupting( + QCoreApplication::translate("QmlDesigner::GenerateResource", "\"%1\" crashed.") + .arg(rccProcess.commandLine().toUserOutput())); + return false; + } + if (rccProcess.exitCode() != 0) { + Core::MessageManager::writeDisrupting( + QCoreApplication::translate("QmlDesigner::GenerateResource", "\"%1\" failed (exit code %2).") + .arg(rccProcess.commandLine().toUserOutput()) + .arg(rccProcess.exitCode())); + return false; + } + return true; +} + +} // namespace QmlDesigner::ResourceGenerator diff --git a/src/plugins/qmldesigner/components/componentcore/resourcegenerator.h b/src/plugins/qmldesigner/components/componentcore/resourcegenerator.h new file mode 100644 index 00000000000..67847634274 --- /dev/null +++ b/src/plugins/qmldesigner/components/componentcore/resourcegenerator.h @@ -0,0 +1,18 @@ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include + +#include + +namespace QmlDesigner::ResourceGenerator { + +QMLDESIGNERCOMPONENTS_EXPORT void generateMenuEntry(QObject *parent); +QMLDESIGNERCOMPONENTS_EXPORT QStringList getProjectFileList(); +QMLDESIGNERCOMPONENTS_EXPORT bool skipSuffix(const QString &fileName); +QMLDESIGNERCOMPONENTS_EXPORT bool createQrcFile(const Utils::FilePath &qrcFilePath); +QMLDESIGNERCOMPONENTS_EXPORT bool createQmlrcFile(const Utils::FilePath &qmlrcFilePath); + +} // namespace QmlDesigner::ResourceGenerator diff --git a/src/plugins/qmldesigner/generateresource.cpp b/src/plugins/qmldesigner/generateresource.cpp deleted file mode 100644 index 44a47e3033f..00000000000 --- a/src/plugins/qmldesigner/generateresource.cpp +++ /dev/null @@ -1,495 +0,0 @@ -// 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 - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include - -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace Utils; - -namespace QmlDesigner { - -QTableWidget* GenerateResource::createFilesTable(const QList &fileNames) -{ - auto table = new QTableWidget(0, 1); - table->setSelectionMode(QAbstractItemView::SingleSelection); - - QStringList labels(QCoreApplication::translate("AddImageToResources","File Name")); - table->setHorizontalHeaderLabels(labels); - table->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch); - table->verticalHeader()->hide(); - table->setShowGrid(false); - - QFont font; - font.setBold(true); - - for (ResourceFile resource : fileNames){ - QString filePath = resource.fileName; - auto checkboxItem = new QTableWidgetItem(); - checkboxItem->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled); - checkboxItem->setCheckState(Qt::Checked); - checkboxItem->setText(filePath); - - if (resource.inProject) - checkboxItem->setFont(font); - - int row = table->rowCount(); - table->insertRow(row); - table->setItem(row, 0, checkboxItem); - } - - return table; -} - -std::optional GenerateResource::getFileList(const QList &fileNames) -{ - std::optional result; - QDialog *dialog = new QDialog(Core::ICore::dialogParent()); - dialog->setMinimumWidth(480); - dialog->setMinimumHeight(640); - - dialog->setModal(true); - dialog->setWindowTitle(QCoreApplication::translate("AddImageToResources","Add Resources")); - QTableWidget *table = createFilesTable(fileNames); - - table->setParent(dialog); - auto mainLayout = new QGridLayout(dialog); - mainLayout->addWidget(table, 0, 0, 1, 4); - - QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok - | QDialogButtonBox::Cancel); - - mainLayout->addWidget(buttonBox, 3, 2, 1, 2); - - QObject::connect(buttonBox, &QDialogButtonBox::accepted, dialog, [dialog]() { - dialog->accept(); - dialog->deleteLater(); - }); - - QObject::connect(buttonBox, &QDialogButtonBox::rejected, dialog, [dialog]() { - dialog->reject(); - dialog->deleteLater(); - }); - - QObject::connect(dialog, &QDialog::accepted, [&result, &table]() { - QStringList fileList; - QString file; - - for (int i = 0; i < table->rowCount(); ++i) { - if (table->item(i,0)->checkState()){ - file = table->item(i,0)->text(); - fileList.append(file); - } - } - - result.emplace(fileList); - }); - - dialog->exec(); - - return result; -} - -bool skipSuffix(const QString &fileName) -{ - const QStringList suffixes = {".pri", - ".pro", - ".user", - ".qrc", - ".qds", - "CMakeLists.txt", - ".db", - ".tmp", - ".TMP", - ".metainfo", - ".qtds", - ".db-shm", - ".db-wal"}; - - for (const auto &suffix : suffixes) - if (fileName.endsWith(suffix)) - return true; - - return false; -} - -QList getFilesFromQrc(QFile *file, bool inProject = false) -{ - QXmlStreamReader reader(file); - QList fileList = {}; - - while (!reader.atEnd()) { - const auto token = reader.readNext(); - - if (token != QXmlStreamReader::StartElement) - continue; - - if (reader.name() == QLatin1String("file")) { - QString fileName = reader.readElementText().trimmed(); - - if ((!fileName.startsWith("./.")) && (!fileName.startsWith("./XXXXXXX")) - && !skipSuffix(fileName)) { - GenerateResource::ResourceFile file; - file.fileName = fileName; - file.inProject = inProject; - fileList.append(file); - } - } - } - return fileList; -} - -static bool runRcc(const CommandLine &command, const FilePath &workingDir, - const QString &resourceFile) -{ - Utils::Process rccProcess; - rccProcess.setWorkingDirectory(workingDir); - rccProcess.setCommand(command); - rccProcess.start(); - if (!rccProcess.waitForStarted()) { - Core::MessageManager::writeDisrupting(QCoreApplication::translate( - "QmlDesigner::GenerateResource", "Unable to generate resource file: %1") - .arg(resourceFile)); - return false; - } - QByteArray stdOut; - QByteArray stdErr; - if (!rccProcess.readDataFromProcess(&stdOut, &stdErr)) { - Core::MessageManager::writeDisrupting( - QCoreApplication::translate("QmlDesigner::GenerateResource", - "A timeout occurred running \"%1\".") - .arg(rccProcess.commandLine().toUserOutput())); - return false; - } - if (!stdOut.trimmed().isEmpty()) - Core::MessageManager::writeFlashing(QString::fromLocal8Bit(stdOut)); - - if (!stdErr.trimmed().isEmpty()) - Core::MessageManager::writeFlashing(QString::fromLocal8Bit(stdErr)); - - if (rccProcess.exitStatus() != QProcess::NormalExit) { - Core::MessageManager::writeDisrupting(QCoreApplication::translate( - "QmlDesigner::GenerateResource", "\"%1\" crashed.") - .arg(rccProcess.commandLine().toUserOutput())); - return false; - } - if (rccProcess.exitCode() != 0) { - Core::MessageManager::writeDisrupting(QCoreApplication::translate( - "QmlDesigner::GenerateResource", "\"%1\" failed (exit code %2).") - .arg(rccProcess.commandLine().toUserOutput()).arg(rccProcess.exitCode())); - return false; - } - return true; -} - -void GenerateResource::generateMenuEntry(QObject *parent) -{ - const Core::Context projectContext(QmlProjectManager::Constants::QML_PROJECT_ID); - // ToDo: move this to QtCreator and add tr to the string then - auto action = new QAction(QCoreApplication::translate("QmlDesigner::GenerateResource", - "Generate QRC Resource File..."), - parent); - action->setEnabled(ProjectExplorer::ProjectManager::startupProject() != nullptr); - // todo make it more intelligent when it gets enabled - QObject::connect(ProjectExplorer::ProjectManager::instance(), - &ProjectExplorer::ProjectManager::startupProjectChanged, - [action]() { - if (auto buildSystem - = QmlProjectManager::QmlBuildSystem::getStartupBuildSystem()) - action->setEnabled(!buildSystem->qtForMCUs()); - }); - - Core::Command *cmd = Core::ActionManager::registerAction(action, "QmlProject.CreateResource"); - QObject::connect(action, &QAction::triggered, [] () { - auto currentProject = ProjectExplorer::ProjectManager::startupProject(); - QTC_ASSERT(currentProject, return); - const FilePath projectPath = currentProject->projectFilePath().parentDir(); - - auto projectFileName = Core::DocumentManager::getSaveFileNameWithExtension( - QCoreApplication::translate("QmlDesigner::GenerateResource", "Save Project as QRC File"), - projectPath.pathAppended(currentProject->displayName() + ".qrc"), - QCoreApplication::translate("QmlDesigner::GenerateResource", - "QML Resource File (*.qrc)")); - if (projectFileName.isEmpty()) - return; - - QTemporaryFile temp(projectPath.toString() + "/XXXXXXX.create.resource.qrc"); - QFile persistentFile(projectFileName.toString()); - - if (!temp.open()) - return; - - temp.close(); - - QtSupport::QtVersion *qtVersion = QtSupport::QtKitAspect::qtVersion( - currentProject->activeTarget()->kit()); - const FilePath rccBinary = qtVersion->rccFilePath(); - - if (!runRcc({rccBinary, {"--project", "--output", temp.fileName()}}, - projectPath, temp.fileName())) { - return; - } - - if (!temp.open()) - return; - - QByteArray firstLine = temp.readLine(); - QList fileList = getFilesFromQrc(&temp); - - QFile existingQrcFile(projectFileName.toString()); - if (existingQrcFile.exists()) { - existingQrcFile.open(QFile::ReadOnly); - fileList = getFilesFromQrc(&existingQrcFile, true); - existingQrcFile.close(); - } - - QDir dir; - dir.setCurrent(projectPath.toString()); - - Utils::FilePaths paths = currentProject->files(ProjectExplorer::Project::AllFiles); - QStringList projectFiles = {}; - - for (const Utils::FilePath &path : paths) { - QString relativepath = dir.relativeFilePath(path.toString()); - - if (!skipSuffix(relativepath)) { - projectFiles.append(relativepath); - - bool found = false; - QString compareString = "./" + relativepath.trimmed(); - for (int i = 0; i < fileList.size(); ++i) - if (fileList.at(i).fileName == compareString) { - fileList[i].inProject = true; - found = true; - break; - } - - if (!found) { - ResourceFile res; - res.fileName = "./" + relativepath.trimmed(); - res.inProject = true; - fileList.append(res); - } - } - } - - temp.close(); - - std::optional modifiedList = getFileList(fileList); - - if (!modifiedList) - return; - - if (!persistentFile.open(QIODevice::ReadWrite | QIODevice::Truncate)) - return; - - QXmlStreamWriter writer(&persistentFile); - writer.setAutoFormatting(true); - writer.setAutoFormattingIndent(0); - - persistentFile.write(firstLine.trimmed()); - writer.writeStartElement("qresource"); - - for (const QString &file : modifiedList.value()) - writer.writeTextElement("file", file.trimmed()); - - writer.writeEndElement(); - persistentFile.write("\n\n"); - persistentFile.close(); - - }); - - // ToDo: move this to QtCreator and add tr to the string then - auto rccAction = new QAction(QCoreApplication::translate("QmlDesigner::GenerateResource", - "Generate Deployable Package..."), - parent); - rccAction->setEnabled(ProjectExplorer::ProjectManager::startupProject() != nullptr); - QObject::connect(ProjectExplorer::ProjectManager::instance(), - &ProjectExplorer::ProjectManager::startupProjectChanged, [rccAction]() { - rccAction->setEnabled(ProjectExplorer::ProjectManager::startupProject()); - }); - - Core::Command *cmd2 = Core::ActionManager::registerAction(rccAction, - "QmlProject.CreateRCCResource"); - QObject::connect(rccAction, &QAction::triggered, []() { - auto currentProject = ProjectExplorer::ProjectManager::startupProject(); - QTC_ASSERT(currentProject, return); - const FilePath projectPath = currentProject->projectFilePath().parentDir(); - - const FilePath resourceFileName = Core::DocumentManager::getSaveFileNameWithExtension( - QCoreApplication::translate("QmlDesigner::GenerateResource", "Save Project as Resource"), - projectPath.pathAppended(currentProject->displayName() + ".qmlrc"), - QCoreApplication::translate("QmlDesigner::GenerateResource", - "QML Resource File (*.qmlrc);;Resource File (*.rcc)")); - if (resourceFileName.isEmpty()) - return; - - Core::MessageManager::writeSilently( - QCoreApplication::translate("QmlDesigner::GenerateResource", - "Generate a resource file out of project %1 to %2") - .arg(currentProject->displayName(), resourceFileName.toUserOutput())); - - QString projectFileName = currentProject->displayName() + ".qrc"; - QFile persistentFile(projectPath.toString() + "/" + projectFileName); - - QTemporaryFile temp(projectPath.toString() + "/XXXXXXX.create.resource.qrc"); - - QtSupport::QtVersion *qtVersion = QtSupport::QtKitAspect::qtVersion( - currentProject->activeTarget()->kit()); - const FilePath rccBinary = qtVersion->rccFilePath(); - - QXmlStreamReader reader; - QByteArray firstLine; - - if (!QFileInfo(persistentFile).exists()) { - if (!temp.open()) - return; - temp.close(); - - if (!runRcc({rccBinary, {"--project", "--output", temp.fileName()}}, - projectPath, resourceFileName.toUserOutput())) { - return; - } - reader.setDevice(&temp); - if (!temp.open()) - return; - firstLine = temp.readLine(); - } else { - reader.setDevice(&persistentFile); - if (!persistentFile.open(QIODevice::ReadWrite)) - return; - - firstLine = persistentFile.readLine(); - } - - QList fileList = {}; - - while (!reader.atEnd()) { - const auto token = reader.readNext(); - - if (token != QXmlStreamReader::StartElement) - continue; - - if (reader.name() == QLatin1String("file")) { - QString fileName = reader.readElementText().trimmed(); - if ((!fileName.startsWith("./.")) && (!fileName.startsWith("./XXXXXXX")) - && !skipSuffix(fileName)) { - ResourceFile file; - file.fileName = fileName; - file.inProject = false; - fileList.append(file); - } - } - } - - QDir dir; - dir.setCurrent(projectPath.toString()); - - Utils::FilePaths paths = currentProject->files(ProjectExplorer::Project::AllFiles); - QStringList projectFiles = {}; - - for (const Utils::FilePath &path : paths) { - QString relativepath = dir.relativeFilePath(path.toString()); - - if (!skipSuffix(relativepath)) { - projectFiles.append(relativepath); - - bool found = false; - QString compareString = "./" + relativepath.trimmed(); - for (int i = 0; i < fileList.size(); ++i) - if (fileList.at(i).fileName == compareString) { - fileList[i].inProject = true; - found = true; - } - - if (!found) { - ResourceFile res; - res.fileName = "./" + relativepath.trimmed(); - res.inProject = true; - fileList.append(res); - } - } - } - - temp.close(); - persistentFile.close(); - std::optional modifiedList = getFileList(fileList); - - if (!modifiedList) - return; - - QTemporaryFile tempFile(projectPath.toString() + "/XXXXXXX.create.modifiedresource.qrc"); - - if (!tempFile.open()) - return; - - QXmlStreamWriter writer(&tempFile); - writer.setAutoFormatting(true); - writer.setAutoFormattingIndent(0); - - tempFile.write(firstLine.trimmed()); - writer.writeStartElement("qresource"); - - for (const QString &file : modifiedList.value()) - writer.writeTextElement("file", file.trimmed()); - - writer.writeEndElement(); - tempFile.write("\n\n"); - tempFile.close(); - - if (!runRcc({rccBinary, {"--binary", "--output", resourceFileName.path(), tempFile.fileName()}}, - projectPath, resourceFileName.path())) { - return; - } - - Core::AsynchronousMessageBox::information( - QCoreApplication::translate("QmlDesigner::GenerateResource", - "Success"), - QCoreApplication::translate("QmlDesigner::GenerateResource", - "Successfully generated deployable package\n %1") - .arg(resourceFileName.toString())); - }); - - Core::ActionContainer *exportMenu = Core::ActionManager::actionContainer( - QmlProjectManager::Constants::EXPORT_MENU); - exportMenu->addAction(cmd, QmlProjectManager::Constants::G_EXPORT_GENERATE); - exportMenu->addAction(cmd2, QmlProjectManager::Constants::G_EXPORT_GENERATE); -} - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/generateresource.h b/src/plugins/qmldesigner/generateresource.h deleted file mode 100644 index cc0c0147a90..00000000000 --- a/src/plugins/qmldesigner/generateresource.h +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once -#include - -namespace QmlDesigner { -namespace GenerateResource { -struct ResourceFile -{ - QString fileName; - bool inProject; -}; - void generateMenuEntry(QObject *parent); - std::optional getFileList(const QList &); - QTableWidget* createFilesTable(const QList &); -} -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/qmldesignerplugin.cpp b/src/plugins/qmldesigner/qmldesignerplugin.cpp index 389f6d41cb1..fda96d2d964 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.cpp +++ b/src/plugins/qmldesigner/qmldesignerplugin.cpp @@ -10,12 +10,12 @@ #include "designmodewidget.h" #include "dynamiclicensecheck.h" #include "exception.h" -#include "generateresource.h" #include "openuiqmlfiledialog.h" #include "qmldesignerconstants.h" #include "qmldesignerexternaldependencies.h" #include "qmldesignerprojectmanager.h" #include "quick2propertyeditorview.h" +#include "resourcegenerator.h" #include "settingspage.h" #include "shortcutmanager.h" #include "toolbar.h" @@ -276,7 +276,7 @@ bool QmlDesignerPlugin::initialize(const QStringList & /*arguments*/, QString *e d = new QmlDesignerPluginPrivate; d->timer.start(); if (Core::ICore::isQtDesignStudio()) - GenerateResource::generateMenuEntry(this); + ResourceGenerator::generateMenuEntry(this); const QString fontPath = Core::ICore::resourcePath( From 6cd5f09941b49855010aa32f537b43d708e881ce Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Wed, 28 Feb 2024 16:50:30 +0100 Subject: [PATCH 127/176] QmlDesigner: Fix warning in PopupDialogButton We should use the templates in this case. Change-Id: Ia727f8c082de36222fff09ee72997c9899e07e97 Reviewed-by: Tim Jenssen Reviewed-by: Qt CI Patch Build Bot Reviewed-by: --- .../imports/NewProjectDialog/PopupDialogButton.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/qtcreator/qmldesigner/newprojectdialog/imports/NewProjectDialog/PopupDialogButton.qml b/share/qtcreator/qmldesigner/newprojectdialog/imports/NewProjectDialog/PopupDialogButton.qml index 8b82443bd07..1f868d77a23 100644 --- a/share/qtcreator/qmldesigner/newprojectdialog/imports/NewProjectDialog/PopupDialogButton.qml +++ b/share/qtcreator/qmldesigner/newprojectdialog/imports/NewProjectDialog/PopupDialogButton.qml @@ -2,7 +2,7 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 import QtQuick -import QtQuick.Controls +import QtQuick.Templates import StudioTheme as StudioTheme Button { From f6025c4e04cbf1aa0e3eda3acddf2b2e569bf6f5 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Mon, 26 Feb 2024 14:58:54 +0100 Subject: [PATCH 128/176] QmlDesigner: Use automatic resource management to color tool Change-Id: I1915577cf06769e69cad41639c5ca170b2d2dae8 Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Patch Build Bot --- .../components/colortool/colortool.cpp | 29 ++++++++++--------- .../components/colortool/colortool.h | 4 ++- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/src/plugins/qmldesigner/components/colortool/colortool.cpp b/src/plugins/qmldesigner/components/colortool/colortool.cpp index 7c7cca6213c..d7695fc6f66 100644 --- a/src/plugins/qmldesigner/components/colortool/colortool.cpp +++ b/src/plugins/qmldesigner/components/colortool/colortool.cpp @@ -27,16 +27,13 @@ namespace QmlDesigner { -ColorTool::ColorTool() -{ -} +ColorTool::ColorTool() = default; ColorTool::~ColorTool() = default; void ColorTool::clear() { - if (m_colorDialog) - m_colorDialog.data()->deleteLater(); + m_colorDialog.reset(); AbstractFormEditorTool::clear(); } @@ -87,7 +84,7 @@ void ColorTool::mouseDoubleClickEvent(const QList &itemList, QGr void ColorTool::itemsAboutToRemoved(const QList &removedItemList) { - if (m_colorDialog.isNull()) + if (!m_colorDialog) return; if (removedItemList.contains(m_formEditorItem)) @@ -96,7 +93,7 @@ void ColorTool::itemsAboutToRemoved(const QList &removedItemLis void ColorTool::selectedItemsChanged(const QList &itemList) { - if (m_colorDialog.data() && m_oldColor.isValid()) + if (m_colorDialog && m_oldColor.isValid()) m_formEditorItem->qmlItemNode().setVariantProperty("color", m_oldColor); if (!itemList.isEmpty() @@ -108,15 +105,19 @@ void ColorTool::selectedItemsChanged(const QList &itemList) else m_oldColor = m_formEditorItem->qmlItemNode().modelValue("color").value(); - if (m_colorDialog.isNull()) { - m_colorDialog = new QColorDialog(view()->formEditorWidget()->parentWidget()); - m_colorDialog.data()->setCurrentColor(m_oldColor); + if (!m_colorDialog) { + m_colorDialog = Utils::makeUniqueObjectLatePtr( + view()->formEditorWidget()->parentWidget()); + m_colorDialog->setCurrentColor(m_oldColor); - connect(m_colorDialog.data(), &QDialog::accepted, this, &ColorTool::colorDialogAccepted); - connect(m_colorDialog.data(), &QDialog::rejected, this, &ColorTool::colorDialogRejected); - connect(m_colorDialog.data(), &QColorDialog::currentColorChanged, this, &ColorTool::currentColorChanged); + connect(m_colorDialog.get(), &QDialog::accepted, this, &ColorTool::colorDialogAccepted); + connect(m_colorDialog.get(), &QDialog::rejected, this, &ColorTool::colorDialogRejected); + connect(m_colorDialog.get(), + &QColorDialog::currentColorChanged, + this, + &ColorTool::currentColorChanged); - m_colorDialog.data()->exec(); + m_colorDialog->exec(); } } else { view()->changeToSelectionTool(); diff --git a/src/plugins/qmldesigner/components/colortool/colortool.h b/src/plugins/qmldesigner/components/colortool/colortool.h index ff0b356c064..966bf856881 100644 --- a/src/plugins/qmldesigner/components/colortool/colortool.h +++ b/src/plugins/qmldesigner/components/colortool/colortool.h @@ -6,6 +6,8 @@ #include "abstractcustomtool.h" #include "selectionindicator.h" +#include + #include #include #include @@ -59,7 +61,7 @@ private: void currentColorChanged(const QColor &color); private: - QPointer m_colorDialog; + Utils::UniqueObjectLatePtr m_colorDialog; FormEditorItem *m_formEditorItem = nullptr; QColor m_oldColor; QString m_oldExpression; From 12760b303274cb6901312fa879836839cda2aca5 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Mon, 26 Feb 2024 16:18:42 +0100 Subject: [PATCH 129/176] QmlDesigner: Add explicit ownership to binding editor It is much better to desribe the ownership explicitly instead of using the hard to read parent child mechanism. Change-Id: I488a6ca1fb08f4bf3b3a540c0bb7d9ffb03895d0 Reviewed-by: Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Patch Build Bot --- .../components/bindingeditor/actioneditor.cpp | 12 ++--- .../components/bindingeditor/actioneditor.h | 4 +- .../bindingeditor/bindingeditor.cpp | 12 ++--- .../components/bindingeditor/bindingeditor.h | 4 +- .../components/bindingeditor/signallist.cpp | 49 ++++++++++--------- .../components/bindingeditor/signallist.h | 6 ++- 6 files changed, 45 insertions(+), 42 deletions(-) diff --git a/src/plugins/qmldesigner/components/bindingeditor/actioneditor.cpp b/src/plugins/qmldesigner/components/bindingeditor/actioneditor.cpp index 256bfac1e9f..5336f6b4dd4 100644 --- a/src/plugins/qmldesigner/components/bindingeditor/actioneditor.cpp +++ b/src/plugins/qmldesigner/components/bindingeditor/actioneditor.cpp @@ -49,12 +49,10 @@ void ActionEditor::prepareDialog() s_lastActionEditor = this; - m_dialog = new ActionEditorDialog(Core::ICore::dialogParent()); + m_dialog = Utils::makeUniqueObjectPtr(Core::ICore::dialogParent()); - QObject::connect(m_dialog, &AbstractEditorDialog::accepted, - this, &ActionEditor::accepted); - QObject::connect(m_dialog, &AbstractEditorDialog::rejected, - this, &ActionEditor::rejected); + QObject::connect(m_dialog.get(), &AbstractEditorDialog::accepted, this, &ActionEditor::accepted); + QObject::connect(m_dialog.get(), &AbstractEditorDialog::rejected, this, &ActionEditor::rejected); m_dialog->setAttribute(Qt::WA_DeleteOnClose); } @@ -327,13 +325,13 @@ void ActionEditor::prepareConnections() for (const QmlModelState &state : QmlItemNode(m_modelNode.view()->rootModelNode()).states().allStates()) states.append(state.name()); - if (!connections.isEmpty() && !m_dialog.isNull()) + if (!connections.isEmpty() && m_dialog) m_dialog->setAllConnections(connections, singletons, states); } void ActionEditor::updateWindowName(const QString &targetName) { - if (!m_dialog.isNull()) { + if (m_dialog) { if (targetName.isEmpty()) m_dialog->setWindowTitle(m_dialog->defaultTitle()); else diff --git a/src/plugins/qmldesigner/components/bindingeditor/actioneditor.h b/src/plugins/qmldesigner/components/bindingeditor/actioneditor.h index 9a14ad2117c..5cdee3e428c 100644 --- a/src/plugins/qmldesigner/components/bindingeditor/actioneditor.h +++ b/src/plugins/qmldesigner/components/bindingeditor/actioneditor.h @@ -9,6 +9,8 @@ #include #include +#include + #include #include #include @@ -63,7 +65,7 @@ private: void prepareDialog(); private: - QPointer m_dialog; + Utils::UniqueObjectPtr m_dialog; QModelIndex m_index; QmlDesigner::ModelNode m_modelNode; }; diff --git a/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.cpp b/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.cpp index b605a77191b..c03c31208fc 100644 --- a/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.cpp +++ b/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.cpp @@ -37,12 +37,10 @@ void BindingEditor::prepareDialog() { QmlDesignerPlugin::emitUsageStatistics(Constants::EVENT_BINDINGEDITOR_OPENED); - m_dialog = new BindingEditorDialog(Core::ICore::dialogParent()); + m_dialog = Utils::makeUniqueObjectPtr(Core::ICore::dialogParent()); - QObject::connect(m_dialog, &AbstractEditorDialog::accepted, - this, &BindingEditor::accepted); - QObject::connect(m_dialog, &AbstractEditorDialog::rejected, - this, &BindingEditor::rejected); + QObject::connect(m_dialog.get(), &AbstractEditorDialog::accepted, this, &BindingEditor::accepted); + QObject::connect(m_dialog.get(), &AbstractEditorDialog::rejected, this, &BindingEditor::rejected); m_dialog->setAttribute(Qt::WA_DeleteOnClose); } @@ -273,13 +271,13 @@ void BindingEditor::prepareBindings() } } - if (!bindings.isEmpty() && !m_dialog.isNull()) + if (!bindings.isEmpty() && m_dialog) m_dialog->setAllBindings(bindings, m_backendValueType); } void BindingEditor::updateWindowName() { - if (!m_dialog.isNull() && m_backendValueType) { + if (m_dialog && m_backendValueType) { QString targetString; if constexpr (useProjectStorage()) { auto exportedTypeNames = m_backendValueType.exportedTypeNamesForSourceId( diff --git a/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.h b/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.h index 52aa0884f69..8ed49b2eb6f 100644 --- a/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.h +++ b/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.h @@ -8,6 +8,8 @@ #include #include +#include + #include #include #include @@ -72,7 +74,7 @@ private: void prepareDialog(); private: - QPointer m_dialog; + Utils::UniqueObjectPtr m_dialog; QVariant m_backendValue; QVariant m_modelNodeBackend; QVariant m_stateModelNode; diff --git a/src/plugins/qmldesigner/components/bindingeditor/signallist.cpp b/src/plugins/qmldesigner/components/bindingeditor/signallist.cpp index a4538b56c31..46470682519 100644 --- a/src/plugins/qmldesigner/components/bindingeditor/signallist.cpp +++ b/src/plugins/qmldesigner/components/bindingeditor/signallist.cpp @@ -54,12 +54,8 @@ bool SignalListFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &s || sourceModel()->data(signalIndex).toString().contains(filterRegularExpression())); } - - - SignalList::SignalList(QObject *) - : m_dialog(QPointer()) - , m_model(new SignalListModel(this)) + : m_model(Utils::makeUniqueObjectPtr(this)) , m_modelNode() { } @@ -71,9 +67,9 @@ SignalList::~SignalList() void SignalList::prepareDialog() { - m_dialog = new SignalListDialog(Core::ICore::dialogParent()); + m_dialog = Utils::makeUniqueObjectPtr(Core::ICore::dialogParent()); m_dialog->setAttribute(Qt::WA_DeleteOnClose); - m_dialog->initialize(m_model); + m_dialog->initialize(m_model.get()); m_dialog->setWindowTitle(::QmlDesigner::SignalList::tr("Signal List for %1") .arg(m_modelNode.validId())); @@ -98,13 +94,14 @@ void SignalList::hideWidget() SignalList* SignalList::showWidget(const ModelNode &modelNode) { - auto signalList = new SignalList(); + auto signalList = new SignalList; signalList->setModelNode(modelNode); signalList->prepareSignals(); signalList->showWidget(); - connect(signalList->m_dialog, &QDialog::destroyed, - [signalList]() { signalList->deleteLater(); } ); + connect(signalList->m_dialog.get(), &QDialog::destroyed, [signalList]() { + signalList->deleteLater(); + }); return signalList; } @@ -265,20 +262,24 @@ void SignalList::removeConnection(const QModelIndex &modelIndex) ModelNode node = targetSignal.parentModelNode(); if (node.isValid()) { - view->executeInTransaction("ConnectionModel::removeConnection", - [this, modelIndex, buttonModelIndex, targetSignal, &node] { - QList allSignals = node.signalProperties(); - if (allSignals.size() > 1) { - const auto targetSignalWithPrefix = SignalHandlerProperty::prefixAdded(targetSignal.name()); - for (const SignalHandlerProperty &signal : allSignals) - if (signal.name() == targetSignalWithPrefix) - node.removeProperty(targetSignalWithPrefix); - } else { - node.destroy(); - } - m_model->setConnected(modelIndex.row(), false); - m_model->setData(buttonModelIndex, QVariant(), SignalListModel::ConnectionsInternalIdRole); - }); + view->executeInTransaction( + "ConnectionModel::removeConnection", + [this, modelIndex, buttonModelIndex, targetSignal, &node] { + const QList allSignals = node.signalProperties(); + if (allSignals.size() > 1) { + const auto targetSignalWithPrefix = SignalHandlerProperty::prefixAdded( + targetSignal.name()); + for (const SignalHandlerProperty &signal : allSignals) + if (signal.name() == targetSignalWithPrefix) + node.removeProperty(targetSignalWithPrefix); + } else { + node.destroy(); + } + m_model->setConnected(modelIndex.row(), false); + m_model->setData(buttonModelIndex, + QVariant(), + SignalListModel::ConnectionsInternalIdRole); + }); } } diff --git a/src/plugins/qmldesigner/components/bindingeditor/signallist.h b/src/plugins/qmldesigner/components/bindingeditor/signallist.h index 228cb0e85ad..5842d445bb6 100644 --- a/src/plugins/qmldesigner/components/bindingeditor/signallist.h +++ b/src/plugins/qmldesigner/components/bindingeditor/signallist.h @@ -8,6 +8,8 @@ #include #include +#include + #include #include #include @@ -74,8 +76,8 @@ private: void removeConnection(const QModelIndex &modelIndex); private: - QPointer m_dialog; - SignalListModel *m_model; + Utils::UniqueObjectPtr m_dialog; + Utils::UniqueObjectPtr m_model; ModelNode m_modelNode; }; From d4426f34246dd3eafcb8fb2ad3c5c3596ae11324 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Tue, 27 Feb 2024 14:22:52 +0100 Subject: [PATCH 130/176] QmlDesignerLite: Add plugin If the Qml Designer Lite plugin is loaded a flag is set in the qml designer base plugin. That flag then can be used to change the qml designer plugin. Task-number: QDS-12044 Change-Id: Ie685a4308637fe6b51a489dcddbe6830fd1a1533 Reviewed-by: Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Patch Build Bot --- src/plugins/CMakeLists.txt | 1 + .../qmldesignerbase/qmldesignerbaseplugin.cpp | 12 +++++++++- .../qmldesignerbase/qmldesignerbaseplugin.h | 6 +++++ src/plugins/qmldesignerlite/CMakeLists.txt | 20 ++++++++++++++++ .../qmldesignerlite/QmlDesignerLite.json.in | 21 +++++++++++++++++ .../qmldesignerlite/qmldesignerliteplugin.cpp | 22 ++++++++++++++++++ .../qmldesignerlite/qmldesignerliteplugin.h | 23 +++++++++++++++++++ 7 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 src/plugins/qmldesignerlite/CMakeLists.txt create mode 100644 src/plugins/qmldesignerlite/QmlDesignerLite.json.in create mode 100644 src/plugins/qmldesignerlite/qmldesignerliteplugin.cpp create mode 100644 src/plugins/qmldesignerlite/qmldesignerliteplugin.h diff --git a/src/plugins/CMakeLists.txt b/src/plugins/CMakeLists.txt index 85de56b4285..04466fed257 100644 --- a/src/plugins/CMakeLists.txt +++ b/src/plugins/CMakeLists.txt @@ -109,6 +109,7 @@ if (WITH_QMLDESIGNER) set(qmldesigner_builddir ${PROJECT_BINARY_DIR}/qmldsgnr) endif() add_subdirectory(qmldesigner ${qmldesigner_builddir}) + add_subdirectory(qmldesignerlite) add_subdirectory(effectcomposer) add_subdirectory(studiowelcome) add_subdirectory(insight) diff --git a/src/plugins/qmldesignerbase/qmldesignerbaseplugin.cpp b/src/plugins/qmldesignerbase/qmldesignerbaseplugin.cpp index 11a180578f4..b71fb63bd89 100644 --- a/src/plugins/qmldesignerbase/qmldesignerbaseplugin.cpp +++ b/src/plugins/qmldesignerbase/qmldesignerbaseplugin.cpp @@ -38,7 +38,7 @@ QmlDesignerBasePlugin *global; QmlDesignerBasePlugin::QmlDesignerBasePlugin() { global = this; -}; +} QmlDesignerBasePlugin::~QmlDesignerBasePlugin() = default; @@ -73,6 +73,16 @@ QByteArray QmlDesignerBasePlugin::experimentalFeaturesSettingsKey() return QByteArray(experimentalFeatures) + version.toLatin1(); } +void QmlDesignerBasePlugin::enbableLiteMode() +{ + global->m_enableLiteMode = true; +} + +bool QmlDesignerBasePlugin::isLiteModeEnabled() +{ + return global->m_enableLiteMode; +} + bool QmlDesignerBasePlugin::initialize(const QStringList &, QString *) { d = std::make_unique(); diff --git a/src/plugins/qmldesignerbase/qmldesignerbaseplugin.h b/src/plugins/qmldesignerbase/qmldesignerbaseplugin.h index 0820c1ef245..a8dc5a7f423 100644 --- a/src/plugins/qmldesignerbase/qmldesignerbaseplugin.h +++ b/src/plugins/qmldesignerbase/qmldesignerbaseplugin.h @@ -24,6 +24,8 @@ public: QmlDesignerBasePlugin(); ~QmlDesignerBasePlugin(); + static QmlDesignerBasePlugin &instance(); + static class DesignerSettings &settings(); static QStyle *style(); static class StudioConfigSettingsPage *studioConfigSettingsPage(); @@ -31,12 +33,16 @@ public: static bool experimentalFeaturesEnabled(); static QByteArray experimentalFeaturesSettingsKey(); + static void enbableLiteMode(); + static bool isLiteModeEnabled(); + private: bool initialize(const QStringList &arguments, QString *errorMessage) override; private: class Data; std::unique_ptr d; + bool m_enableLiteMode = false; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesignerlite/CMakeLists.txt b/src/plugins/qmldesignerlite/CMakeLists.txt new file mode 100644 index 00000000000..95c1035b09b --- /dev/null +++ b/src/plugins/qmldesignerlite/CMakeLists.txt @@ -0,0 +1,20 @@ +if(BUILD_DESIGNSTUDIO AND ($ OR WITH_TESTS)) + set(ENABLE_COMPILE_WARNING_AS_ERROR_DEFAULT ON) +else() + set(ENABLE_COMPILE_WARNING_AS_ERROR_DEFAULT OFF) +endif() +env_with_default("QDS_ENABLE_COMPILE_WARNING_AS_ERROR" ENV_ENABLE_COMPILE_WARNING_AS_ERROR + ${ENABLE_COMPILE_WARNING_AS_ERROR_DEFAULT}) +option(ENABLE_COMPILE_WARNING_AS_ERROR "Treat warnings as errors in QmlDesigner" ${ENV_ENABLE_COMPILE_WARNING_AS_ERROR}) +add_feature_info("Treat warnings as errors in QmlDesigner" ${ENABLE_COMPILE_WARNING_AS_ERROR} "") + +add_qtc_plugin(QmlDesignerLite + PLUGIN_DEPENDS QmlDesigner + SOURCES + qmldesignerliteplugin.cpp qmldesignerliteplugin.h +) + +extend_qtc_plugin(QmlDesignerBase + CONDITION ENABLE_COMPILE_WARNING_AS_ERROR + PROPERTIES COMPILE_WARNING_AS_ERROR ON +) diff --git a/src/plugins/qmldesignerlite/QmlDesignerLite.json.in b/src/plugins/qmldesignerlite/QmlDesignerLite.json.in new file mode 100644 index 00000000000..75410dfd586 --- /dev/null +++ b/src/plugins/qmldesignerlite/QmlDesignerLite.json.in @@ -0,0 +1,21 @@ +{ + "Name" : "QmlDesignerLite", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "Vendor" : "The Qt Company Ltd", + "Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt Commercial License Agreement provided with the Software or, alternatively, in accordance with the terms contained in a written agreement between you and The Qt Company.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin may be used under the terms of the GNU General Public License version 3 as published by the Free Software Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT included in the packaging of this plugin. Please review the following information to ensure the GNU General Public License requirements will be met: https://www.gnu.org/licenses/gpl-3.0.html." + ], + "Category" : "Qt Quick", + "Description" : "Qml Designer Lite", + "LongDescription": "Qml Designer Lite is a lightweight version of Qt Design Studio, providing a subset of the features of the full Qt Design Studio.", + "Url" : "http://www.qt.io", + "Experimental": true, + ${IDE_PLUGIN_DEPENDENCIES} +} diff --git a/src/plugins/qmldesignerlite/qmldesignerliteplugin.cpp b/src/plugins/qmldesignerlite/qmldesignerliteplugin.cpp new file mode 100644 index 00000000000..4414b92f7ca --- /dev/null +++ b/src/plugins/qmldesignerlite/qmldesignerliteplugin.cpp @@ -0,0 +1,22 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +#include "qmldesignerliteplugin.h" + +#include + +namespace QmlDesigner { + +QmlDesignerLitePlugin::QmlDesignerLitePlugin() +{ + QmlDesignerBasePlugin::enbableLiteMode(); +} + +QmlDesignerLitePlugin::~QmlDesignerLitePlugin() = default; + +bool QmlDesignerLitePlugin::initialize(const QStringList &, QString *) +{ + return true; +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesignerlite/qmldesignerliteplugin.h b/src/plugins/qmldesignerlite/qmldesignerliteplugin.h new file mode 100644 index 00000000000..4fb30e60c46 --- /dev/null +++ b/src/plugins/qmldesignerlite/qmldesignerliteplugin.h @@ -0,0 +1,23 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +#pragma once + +#include + +namespace QmlDesigner { + +class QmlDesignerLitePlugin final : public ExtensionSystem::IPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "QmlDesignerLite.json") + +public: + QmlDesignerLitePlugin(); + ~QmlDesignerLitePlugin(); + +private: + bool initialize(const QStringList &arguments, QString *errorMessage) override; +}; + +} // namespace QmlDesigner From b3718ad783d42ab7e1ef502492dec324a750ca79 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 29 Feb 2024 16:37:20 +0100 Subject: [PATCH 131/176] QmlDesigner: Enforce lite plugin if lite is enabled You need to remove the build or set the project storage option too. Task-number: QDS-12102 Change-Id: I45eb30fa18f56c170344afdaf94275b944230fa5 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Reviewed-by: Thomas Hartmann --- src/plugins/qmldesigner/CMakeLists.txt | 10 +++++++--- .../designercore/include/qmldesignercorelib_global.h | 9 +++++++++ src/plugins/qmldesigner/qmldesignerplugin.cpp | 12 +++++++++++- 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index bb274f7703a..66af859a61b 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -10,9 +10,11 @@ endif() add_compile_options("$<$:-Wno-error=maybe-uninitialized>") -env_with_default("QDS_USE_PROJECTSTORAGE" ENV_QDS_USE_PROJECTSTORAGE OFF) -option(USE_PROJECTSTORAGE "Use ProjectStorage" ${ENV_QDS_USE_PROJECTSTORAGE}) -add_feature_info("ProjectStorage" ${USE_PROJECTSTORAGE} "") +set(BUILD_NOT_DESIGNSTUDIO NOT ${BUILD_NOT_DESIGNSTUDIO}) +option(QTC_USE_QML_DESIGNER_LITE "Use Qml Designer Lite" ${BUILD_NOT_DESIGNSTUDIO}) +add_feature_info("Qml Designer Lite" ${QTC_USE_QML_DESIGNER_LITE} "") + +option(USE_PROJECTSTORAGE "Use ProjectStorage" ${QTC_USE_QML_DESIGNER_LITE}) env_with_default("QTC_ENABLE_PROJECT_STORAGE_TRACING" ENV_QTC_ENABLE_PROJECT_STORAGE_TRACING OFF) option(ENABLE_PROJECT_STORAGE_TRACING "Enable sqlite tracing" ${ENV_QTC_ENABLE_PROJECT_STORAGE_TRACING}) @@ -79,6 +81,7 @@ add_qtc_library(QmlDesignerCore STATIC Sqlite DEFINES $<$:QDS_USE_PROJECTSTORAGE> + $<$:QTC_USE_QML_DESIGNER_LITE> INCLUDES ${CMAKE_CURRENT_LIST_DIR} PUBLIC_INCLUDES @@ -484,6 +487,7 @@ add_qtc_plugin(QmlDesigner IDE_LIBRARY_BASENAME=\"${IDE_LIBRARY_BASE_PATH}\" SHARE_QML_PATH="${CMAKE_CURRENT_SOURCE_DIR}/../../../share/qtcreator/qmldesigner" $<$:QDS_USE_PROJECTSTORAGE> + $<$:QTC_USE_QML_DESIGNER_LITE> INCLUDES ${CMAKE_CURRENT_LIST_DIR}/components ${CMAKE_CURRENT_LIST_DIR}/components/assetslibrary diff --git a/src/plugins/qmldesigner/designercore/include/qmldesignercorelib_global.h b/src/plugins/qmldesigner/designercore/include/qmldesignercorelib_global.h index 7871b5df3a6..57ef521d867 100644 --- a/src/plugins/qmldesigner/designercore/include/qmldesignercorelib_global.h +++ b/src/plugins/qmldesigner/designercore/include/qmldesignercorelib_global.h @@ -33,4 +33,13 @@ enum AnchorLineType { AnchorLineAllMask = AnchorLineVerticalMask | AnchorLineHorizontalMask }; +constexpr bool isUsingQmlDesignerLite() +{ +#ifdef QTC_USE_QML_DESIGNER_LITE + return true; +#else + return false; +#endif +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/qmldesignerplugin.cpp b/src/plugins/qmldesigner/qmldesignerplugin.cpp index fda96d2d964..cd0ffaba263 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.cpp +++ b/src/plugins/qmldesigner/qmldesignerplugin.cpp @@ -73,10 +73,11 @@ #include #include +#include #include #include #include -#include +#include #include #include #include @@ -259,6 +260,15 @@ QmlDesignerPlugin::~QmlDesignerPlugin() //////////////////////////////////////////////////// bool QmlDesignerPlugin::initialize(const QStringList & /*arguments*/, QString *errorMessage/* = 0*/) { + if constexpr (isUsingQmlDesignerLite()) { + if (!QmlDesignerBasePlugin::isLiteModeEnabled()) { + QMessageBox::warning(Core::ICore::dialogParent(), + tr("Qml Designer Lite"), + tr("The Qml Designer Lite plugin is not enabled.")); + return false; + } + } + Sqlite::LibraryInitializer::initialize(); QDir{}.mkpath(Core::ICore::cacheResourcePath().toString()); From 968b88edf93fb5611eb7764cbae2de85da4cb1fb Mon Sep 17 00:00:00 2001 From: Teea Poldsam Date: Tue, 27 Feb 2024 13:26:55 +0200 Subject: [PATCH 132/176] Doc: Update Log In UI - States Edited the tutorial and added some images. Fixes: QDS-11190 Change-Id: Idb25b9bbe2274185ff279528fa6815a0a74abb94 Reviewed-by: Johanna Vanhatapio --- .../doc/images/loginui3-base-state.webp | Bin 0 -> 11530 bytes .../images/loginui3-login-state-preview.jpg | Bin 46522 -> 0 bytes .../images/loginui3-login-state-preview.webp | Bin 0 -> 9218 bytes .../doc/images/loginui3-login-state.jpg | Bin 31280 -> 0 bytes .../doc/images/loginui3-login-state.webp | Bin 0 -> 6856 bytes .../loginui3-new-entryfield-properties.webp | Bin 0 -> 9270 bytes .../examples/doc/images/loginui3-states.jpg | Bin 35801 -> 0 bytes .../examples/doc/images/loginui3-states.webp | Bin 0 -> 8800 bytes doc/qtdesignstudio/examples/doc/loginui3.qdoc | 108 +++++------------- 9 files changed, 31 insertions(+), 77 deletions(-) create mode 100644 doc/qtdesignstudio/examples/doc/images/loginui3-base-state.webp delete mode 100644 doc/qtdesignstudio/examples/doc/images/loginui3-login-state-preview.jpg create mode 100644 doc/qtdesignstudio/examples/doc/images/loginui3-login-state-preview.webp delete mode 100644 doc/qtdesignstudio/examples/doc/images/loginui3-login-state.jpg create mode 100644 doc/qtdesignstudio/examples/doc/images/loginui3-login-state.webp create mode 100644 doc/qtdesignstudio/examples/doc/images/loginui3-new-entryfield-properties.webp delete mode 100644 doc/qtdesignstudio/examples/doc/images/loginui3-states.jpg create mode 100644 doc/qtdesignstudio/examples/doc/images/loginui3-states.webp diff --git a/doc/qtdesignstudio/examples/doc/images/loginui3-base-state.webp b/doc/qtdesignstudio/examples/doc/images/loginui3-base-state.webp new file mode 100644 index 0000000000000000000000000000000000000000..756e20da0685f21a3d2bee77442f18571b8a95d5 GIT binary patch literal 11530 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 literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/examples/doc/images/loginui3-login-state-preview.jpg b/doc/qtdesignstudio/examples/doc/images/loginui3-login-state-preview.jpg deleted file mode 100644 index 26159f9be4846bff4d00f5a5ff1f6521530974e6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 46522 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

    GgzEU(p?6 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 diff --git a/doc/qtdesignstudio/examples/doc/images/loginui3-login-state.webp b/doc/qtdesignstudio/examples/doc/images/loginui3-login-state.webp new file mode 100644 index 0000000000000000000000000000000000000000..6b9ffad8367d07e927301797b27026868e2062f4 GIT binary patch literal 6856 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 wa@?=mMZx{X`?hGP2GlvQ-tBxXl&|+wTqrsOB3 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 diff --git a/doc/qtdesignstudio/examples/doc/images/loginui3-states.webp b/doc/qtdesignstudio/examples/doc/images/loginui3-states.webp new file mode 100644 index 0000000000000000000000000000000000000000..b4eb250f0ee0fd98d89c71c84a3154e0c018de67 GIT binary patch literal 8800 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 literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/examples/doc/loginui3.qdoc b/doc/qtdesignstudio/examples/doc/loginui3.qdoc index c66d2b7e484..a6b978d3d67 100644 --- a/doc/qtdesignstudio/examples/doc/loginui3.qdoc +++ b/doc/qtdesignstudio/examples/doc/loginui3.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! @@ -17,7 +17,7 @@ some basic UI components, such as pages, buttons, and entry fields. The third tutorial in the series describes how to use \e states to add a second page to the UI. On the first page, users can enter a username - and password to log in. On the second page, they can register if they + and password to log in. On the second page, they can register as users if they do not already have an account. Because the second page will contain most of the same UI components as the @@ -25,25 +25,22 @@ when a user selects the \e {Create Account} button. The starting point for this tutorial is the completed - \l{Log In UI - Positioning} project. You can download the project from + \l{Log In UI - Positioning} project. You can download the project \l{https://git.qt.io/public-demos/qtdesign-studio/-/tree/master/tutorial%20projects/Loginui2}{here}. - Additionally, you can download the completed project of this tutorial from + Additionally, you can download the completed project of this tutorial \l{https://git.qt.io/public-demos/qtdesign-studio/-/tree/master/tutorial%20projects/Loginui3}{here}. - The \e {Learn More} sections provide additional information relevant to the - task at hand. + For more information, see \l{Working with states} and \l{Signal and Handler Event System}. \section1 Adding UI Components You will add another entry field for verifying the password that users enter to create an account. You are already familiar with the tasks in - this section from previous parts of the tutorial. + this section from the previous \e {Login UI} tutorials. - To preview the changes that you make to the UI while you make - them, select the \inlineimage icons/live_preview.png - (\uicontrol {Show Live Preview}) button on the \l {2D} view - toolbar or press \key {Alt+P}. + To preview the changes that you make, select the \uicontrol {Live Preview} button + or press \key {Alt+P}. To add the entry field needed on the second page to the \e Screen01 component: @@ -51,16 +48,17 @@ \list 1 \li Double-click \e Screen01.ui.qml in \uicontrol Projects to open it in the \uicontrol {2D} view. - \li Drag-and-drop an instance of the EntryField component from + \li Drag an instance of the EntryField component from \uicontrol Components > \uicontrol {My Components} to \e fields in \l Navigator. \li Select the EntryField instance in \uicontrol Navigator to modify - its ID and text in \uicontrol Properties. + its ID and text in \uicontrol Properties. \li In \uicontrol Component > \uicontrol ID, enter \e repeatPassword. - \li In \uicontrol {Button Content} > \uicontrol Text, enter - \e {Repeat Password} and select \uicontrol tr to mark the text + \li In \uicontrol {EntryField} > \uicontrol {Button Content} > \uicontrol Text, + enter \e {Repeat Password} and select \uicontrol tr to mark the text translatable. + \image loginui3-new-entryfield-properties.webp "Entryfield properties" \li Select \uicontrol File > \uicontrol Save or press \key {Ctrl+S} to save your changes. \endlist @@ -68,10 +66,10 @@ \e Screen01 should now look something like this in the \uicontrol {2D} view: - \image loginui3-base-state.jpg "Login page with a Repeat Password field" + \image loginui3-base-state.webp "Login page with a Repeat Password field" Next, you will add states for the login page and the account creation page, - where you use the visibility property to hide the repeat password field on + where you use the visibility property to hide the \e {Repeat Password} field on the login page and the \e {Create Account} button on the account creation page. @@ -84,23 +82,20 @@ \li In the \l States view, select \inlineimage icons/plus.png . \li Enter \e login as the state name. - \image loginui3-login-state.jpg "States view" + \image loginui3-login-state.webp "States view" \li Select \e repeatPassword in \uicontrol Navigator to display its properties in \uicontrol Properties. - \li In the \uicontrol Visibility section, deselect the - \uicontrol Visible check box to hide the repeat - password field in the login state. + \li In the \uicontrol Visibility section, clear the + \uicontrol Visible checkbox to hide the \e {Repeat Password} field in the login state. \image loginui3-visibility.png - \li In \uicontrol States, select \inlineimage icons/action-icon.png - for \e login to open the \uicontrol Actions menu, and then - select \uicontrol {Set as Default} to determine that the \e login - state is applied when the application starts. + \li In \uicontrol States, select \uicontrol Default for \e login to determine that the + \e login state is applied when the application starts. \li With the base state selected, add another state and name it \e createAccount. This state should now look identical to the base state. - \li Select \e createAccount in the \uicontrol Navigator to display its + \li Select the \e createAccount button in \uicontrol Navigator to display its properties in \uicontrol Properties. - \li In \uicontrol Visibility, deselect the \uicontrol Visible check box + \li In \uicontrol Visibility, clear the \uicontrol Visible checkbox to hide the \e {Create Account} button in the account creation state. \li Select \uicontrol File > \uicontrol Save or press \key {Ctrl+S} @@ -109,14 +104,14 @@ You can now see all the states in the \uicontrol States view: - \image loginui3-states.jpg "All states in the States view" + \image loginui3-states.webp "All states in the States view" The live preview displays the default state, \e login: - \image loginui3-login-state-preview.jpg "Preview of the login state" + \image loginui3-login-state-preview.webp "Preview of the login state" Next, you will create connections to specify that clicking the - \uicontrol {Create Account} button on the login page triggers a + \e {Create Account} button on the login page triggers a transition to the account creation page. \section1 Connecting Buttons to States @@ -137,7 +132,7 @@ \li In the \uicontrol Connections tab, select the \inlineimage icons/plus.png button to open the connection setup options. \li Set \uicontrol Signal to \c clicked, \uicontrol Action to - \c {Change State}, \uicontrol {State Group} to \c rectangle and + \c {Change State}, \uicontrol {State Group} to \c rectangle, and \uicontrol State to \c createAccount in the respective drop-down menus. \li Select the \inlineimage icons/close.png @@ -148,60 +143,19 @@ \li Select \uicontrol File > \uicontrol Save or press \key {Ctrl+S} to save your changes. - \note Or, you can right-click the \e createAccount button in \l Navigator. - Then select \uicontrol {Connections} > \uicontrol {Add signal handler} > - \uicontrol {clicked} > \uicontrol {Change State to createAccount}. + \note Alternatively, right-click the \e createAccount button in + \uicontrol Navigator. Then select \uicontrol {Connections} > + \uicontrol {Add new Connection} > \uicontrol {clicked} > + \uicontrol {Change State to createAccount}. \endlist - In the live preview, you can now click the \uicontrol {Create Account} + In the live preview, you can now click the \e {Create Account} button to go to the account creation page. \image loginui3.gif "Moving between login page and account creation page" - \section1 Learn More - \section2 States - The \l{Working with States}{state} of a particular visual component is the set of - information which describes how and where the individual parts of the visual - component are displayed within it, and all the data associated with that - state. Most visual components in a UI will have a limited number of states, - each with well-defined properties. - - For example, a list item may be either selected or not, and if - selected, it may either be the currently active single selection or it - may be part of a selection group. Each of those states may have certain - associated visual appearance (neutral, highlighted, expanded, and so on). - - Youn can apply states to trigger behavior or animations. UI components - typically have a default state that contains all of a component's initial - property values and is therefore useful for managing property values before - state changes. - - You can specify additional states by adding new states. Each state within a - component has a unique name. To change the current state of an component, - the state property is set to the name of the state. State changes can be - bound to conditions by using the \c when property. - - \section2 Signal and Event Handlers - - UI components need to communicate with each other. For example, a button - needs to know that the user has clicked on it. In response, the button may - change color to indicate its state and perform an action. - - To accomplish this, a \e {signal-and-handler} mechanism is used, where the - \e signal is the event that is responded to through a \e {signal handler}. - When a signal is emitted, the corresponding signal handler is invoked. - Placing logic, such as a script or other operations, in the handler allows - the component to respond to the event. - - For more information, see \l{Signal and Handler Event System}. - \section1 Next Steps - For a more complicated UI, you would typically use components that specify - a view of items provided by a model, such as a \l{List and Grid Views} - {List View} or \l StackView. For more information, see - \l{Lists and Other Data Models}. - To learn how to use a timeline to animate the transition between the login and account creation pages, see the next tutorial in the series, \l {Log In UI - Timeline}. From f6a377a3227b00e32f79b3eb692d799e0d5c1ec7 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Mon, 4 Mar 2024 17:26:01 +0100 Subject: [PATCH 133/176] QmlDesigner: Fix regression in ConnectionModelBackendDelegate Since we do not set the parent anymore, we have to keep the model as a member. Change-Id: I1a850b82366c4c1e26c4b5fe42e61f40adf47baf Reviewed-by: Marco Bubke Reviewed-by: Qt CI Patch Build Bot --- .../connectioneditor/connectionmodel.cpp | 16 ++++++++-------- .../connectioneditor/connectionmodel.h | 1 + 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp index d0d6d8f060c..8b649e5a9a4 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp @@ -683,7 +683,7 @@ ConnectionModelBackendDelegate::ConnectionModelBackendDelegate(ConnectionModel * , m_conditionListModel(model) , m_propertyTreeModel(model->connectionView()) , m_propertyListProxyModel(&m_propertyTreeModel) - + , m_model(model) { connect(&m_signalDelegate, &PropertyTreeModelDelegate::commitData, this, [this] { handleTargetChanged(); @@ -737,7 +737,7 @@ void ConnectionModelBackendDelegate::changeActionType(ActionType actionType) { QTC_ASSERT(actionType != ConnectionModelStatementDelegate::Custom, return ); - ConnectionModel *model = qobject_cast(parent()); + ConnectionModel *model = m_model; QTC_ASSERT(model, return ); QTC_ASSERT(model->connectionView()->isAttached(), return ); @@ -901,7 +901,7 @@ void ConnectionModelBackendDelegate::update() m_propertyTreeModel.resetModel(); m_propertyListProxyModel.setRowAndInternalId(0, internalRootIndex); - ConnectionModel *model = qobject_cast(parent()); + ConnectionModel *model = m_model; QTC_ASSERT(model, return ); if (!model->connectionView()->isAttached()) @@ -940,7 +940,7 @@ void ConnectionModelBackendDelegate::update() void ConnectionModelBackendDelegate::jumpToCode() { - ConnectionModel *model = qobject_cast(parent()); + ConnectionModel *model = m_model; QTC_ASSERT(model, return ); QTC_ASSERT(model->connectionView()->isAttached(), return ); @@ -1053,7 +1053,7 @@ void ConnectionModelBackendDelegate::setupCondition() void ConnectionModelBackendDelegate::setupHandlerAndStatements() { - ConnectionModel *model = qobject_cast(parent()); + ConnectionModel *model = m_model; QTC_ASSERT(model, return ); SignalHandlerProperty signalHandlerProperty = model->signalHandlerPropertyForRow(currentRow()); @@ -1109,7 +1109,7 @@ void ConnectionModelBackendDelegate::setupHandlerAndStatements() void ConnectionModelBackendDelegate::handleTargetChanged() { - ConnectionModel *model = qobject_cast(parent()); + ConnectionModel *model = m_model; QTC_ASSERT(model, return ); @@ -1182,8 +1182,8 @@ void ConnectionModelBackendDelegate::handleKOStatementChanged() void ConnectionModelBackendDelegate::handleConditionChanged() { + ConnectionModel *model = m_model; - ConnectionModel *model = qobject_cast(parent()); QTC_ASSERT(model, return ); QTC_ASSERT(model->connectionView()->isAttached(), return ); @@ -1197,7 +1197,7 @@ void ConnectionModelBackendDelegate::handleConditionChanged() void ConnectionModelBackendDelegate::commitNewSource(const QString &source) { - ConnectionModel *model = qobject_cast(parent()); + ConnectionModel *model = m_model; QTC_ASSERT(model, return ); diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h index 161b677c004..017cf676843 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h @@ -264,6 +264,7 @@ private: PropertyTreeModel m_propertyTreeModel; PropertyListProxyModel m_propertyListProxyModel; bool m_blockReflection = false; + QPointer m_model = nullptr; }; class ConnectionModel : public QStandardItemModel From cbd91b01671409c37a0cb16335e9b1f435bb1d3b Mon Sep 17 00:00:00 2001 From: Teea Poldsam Date: Fri, 1 Mar 2024 08:25:42 +0200 Subject: [PATCH 134/176] Doc: Update Log In UI - Timeline Edited the tutorial and added some images. Fixes: QDS-11191 Change-Id: Icfc9bda96472a7d5bd82822ff5dac66c6a5c5849 Reviewed-by: Johanna Vanhatapio --- .../doc/images/loginui4-base-state.webp | Bin 0 -> 11526 bytes ...oginui4-easing-curve-top-anchor-margin.png | Bin 11258 -> 0 bytes ...ginui4-easing-curve-top-anchor-margin.webp | Bin 0 -> 7572 bytes .../doc/images/loginui4-hierarchy.webp | Bin 0 -> 3790 bytes .../doc/images/loginui4-password-layout.webp | Bin 0 -> 5688 bytes .../examples/doc/images/loginui4-states.webp | Bin 0 -> 8696 bytes .../doc/images/loginui4-timeline-all.webp | Bin 0 -> 7398 bytes .../doc/images/loginui4-timeline-final.webp | Bin 0 -> 5550 bytes .../doc/images/loginui4-timeline-opacity.png | Bin 8125 -> 0 bytes .../doc/images/loginui4-timeline-opacity.webp | Bin 0 -> 7134 bytes .../examples/doc/images/loginui4-timeline.png | Bin 9946 -> 0 bytes .../doc/images/loginui4-timeline.webp | Bin 0 -> 6542 bytes .../doc/images/loginui4-username-layout.webp | Bin 0 -> 5666 bytes doc/qtdesignstudio/examples/doc/loginui4.qdoc | 226 ++++++++---------- .../images/icons/more-button.png | Bin 0 -> 1558 bytes 15 files changed, 103 insertions(+), 123 deletions(-) create mode 100644 doc/qtdesignstudio/examples/doc/images/loginui4-base-state.webp delete mode 100644 doc/qtdesignstudio/examples/doc/images/loginui4-easing-curve-top-anchor-margin.png create mode 100644 doc/qtdesignstudio/examples/doc/images/loginui4-easing-curve-top-anchor-margin.webp create mode 100644 doc/qtdesignstudio/examples/doc/images/loginui4-hierarchy.webp create mode 100644 doc/qtdesignstudio/examples/doc/images/loginui4-password-layout.webp create mode 100644 doc/qtdesignstudio/examples/doc/images/loginui4-states.webp create mode 100644 doc/qtdesignstudio/examples/doc/images/loginui4-timeline-all.webp create mode 100644 doc/qtdesignstudio/examples/doc/images/loginui4-timeline-final.webp delete mode 100644 doc/qtdesignstudio/examples/doc/images/loginui4-timeline-opacity.png create mode 100644 doc/qtdesignstudio/examples/doc/images/loginui4-timeline-opacity.webp delete mode 100644 doc/qtdesignstudio/examples/doc/images/loginui4-timeline.png create mode 100644 doc/qtdesignstudio/examples/doc/images/loginui4-timeline.webp create mode 100644 doc/qtdesignstudio/examples/doc/images/loginui4-username-layout.webp create mode 100644 doc/qtdesignstudio/images/icons/more-button.png diff --git a/doc/qtdesignstudio/examples/doc/images/loginui4-base-state.webp b/doc/qtdesignstudio/examples/doc/images/loginui4-base-state.webp new file mode 100644 index 0000000000000000000000000000000000000000..ca2f60773567602bed2e98adba78c6631edc7ef7 GIT binary patch literal 11526 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 literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/examples/doc/images/loginui4-hierarchy.webp b/doc/qtdesignstudio/examples/doc/images/loginui4-hierarchy.webp new file mode 100644 index 0000000000000000000000000000000000000000..48e5847f7a8ccbc24d7b7574f6a088d3f6a251ba GIT binary patch literal 3790 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 literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/examples/doc/images/loginui4-password-layout.webp b/doc/qtdesignstudio/examples/doc/images/loginui4-password-layout.webp new file mode 100644 index 0000000000000000000000000000000000000000..c19963bc3e436d182044f2c69e879cce28c1d4c0 GIT binary patch literal 5688 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 Yu#aB0WcrEFXW!(v|KIUfU6hdr0G=Ux?*IS* literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/examples/doc/images/loginui4-timeline-final.webp b/doc/qtdesignstudio/examples/doc/images/loginui4-timeline-final.webp new file mode 100644 index 0000000000000000000000000000000000000000..028d3e00ff826f63d038a83ab90b0ace2483d3b1 GIT binary patch literal 5550 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 diff --git a/doc/qtdesignstudio/examples/doc/images/loginui4-timeline-opacity.webp b/doc/qtdesignstudio/examples/doc/images/loginui4-timeline-opacity.webp new file mode 100644 index 0000000000000000000000000000000000000000..c1543c404c6fd25b765b70033569f50280711784 GIT binary patch literal 7134 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 CrQ4AJ literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/examples/doc/images/loginui4-timeline.png b/doc/qtdesignstudio/examples/doc/images/loginui4-timeline.png deleted file mode 100644 index 67b0c041b70acc79d7c4ed7a2458d28004e91a27..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9946 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 diff --git a/doc/qtdesignstudio/examples/doc/images/loginui4-timeline.webp b/doc/qtdesignstudio/examples/doc/images/loginui4-timeline.webp new file mode 100644 index 0000000000000000000000000000000000000000..a13c5a8d2c4a3d20408953f7af1184251a5b6a62 GIT binary patch literal 6542 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 SmE3{@X1=lqRfeDKDop?|+k9#O literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/examples/doc/images/loginui4-username-layout.webp b/doc/qtdesignstudio/examples/doc/images/loginui4-username-layout.webp new file mode 100644 index 0000000000000000000000000000000000000000..ecff1f2521135ab9374bb5d117de87de8296d6e5 GIT binary patch literal 5666 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 \inlineimage icons/more-button.png + > \uicontrol Delete to remove the state. Repeat for the \e createAccount state. + \li Select \e username in \l Navigator, and then select: + \list + \li \inlineimage icons/arrowleft.png to move \e username into the parent + rectangle. + \li \inlineimage icons/navigator-arrowup.png to move \e username below + \e tagLine in \uicontrol Navigator to preserve the + \l{Arranging Components}{component hierarchy}. + \endlist + \li Repeat the previous step for \e password and \e repeatPassword. \li Select \e fields in \uicontrol Navigator and press \key Delete to delete it. - \li Select \e username in \uicontrol Navigator to display its properties - in \l Properties. - \li Select \uicontrol Layout > \inlineimage icons/anchor-top.png - to anchor the top of \e username to the bottom of \e tagLine in the - \uicontrol Target field. \QDS will suggest an appropriate margin - based on the current position of the field on the y axis, 170 - pixels. - \li Select \inlineimage icons/anchor-center-horizontal.png - to anchor \e username horizontally to its parent in the - \uicontrol Target field. - \li Select \e password in \uicontrol Navigator to display its properties - in \uicontrol Properties. - \li Select \uicontrol Layout > \inlineimage icons/anchor-top.png - to anchor the top of \e password to the bottom of \e username in - the \uicontrol Target field with a 20-pixel margin. - \li Select \inlineimage icons/anchor-center-horizontal.png - to anchor \e password horizontally to its parent in the - \uicontrol Target field. - \li Repeat the above steps to anchor the top of \e repeatPassword - to the bottom of \e password with a 20-pixel margin and to + \image loginui4-hierarchy.webp "The hierarchy of the components." + \li Select \e username in \uicontrol Navigator. + \li In \uicontrol Properties > \uicontrol Layout, set: + \list + \li \uicontrol Anchors to \inlineimage icons/anchor-top.png and + \inlineimage icons/anchor-center-horizontal.png. + \li \uicontrol Target \inlineimage icons/anchor-top.png to + \e tagLine to anchor \e username to the tag line. + \li \uicontrol Margin \inlineimage icons/anchor-top.png to \e 170 and + select \inlineimage icons/anchor-bottom.png to anchor \e username to the + bottom of the target. + \note Selecting the anchor button should automatically update the + \uicontrol {2D} view. If it doesn't, select \inlineimage icons/reset.png + on the \uicontrol {2D} view toolbar to refresh the \uicontrol{2D} view. + \endlist + This attaches \e username to the bottom of the tag line while keeping its + horizontal center aligned with that of the rectangle. + \image loginui4-username-layout.webp "The layout of username entry field." + \li Select \e password in \uicontrol Navigator. + \li In \uicontrol Properties > \uicontrol Layout, set: + \list + \li \uicontrol Anchors to \inlineimage icons/anchor-top.png and + \inlineimage icons/anchor-center-horizontal.png. + \li \uicontrol Target \inlineimage icons/anchor-top.png to + \e username to anchor \e password to \e username. + \li \uicontrol Margin \inlineimage icons/anchor-top.png to \e 20 and + select \inlineimage icons/anchor-bottom.png to anchor \e password to the + bottom of the target. + \endlist + This attaches \e password to the bottom of \e username while keeping its + horizontal center aligned with that of the rectangle. + \image loginui4-password-layout.webp "The layout of password entry field." + \li Repeat the previous step to anchor the top of \e repeatPassword + to the bottom of \e password with a margin of \e 20 and to anchor it horizontally to its parent. \li Select \uicontrol File > \uicontrol Save or press \key {Ctrl+S} to save your changes. \endlist - You could also animate the y-position property of the repeat password - field for a similar effect. In that case, you would need to use absolute - positioning for the field. This is less flexible if you export your - design from a design tool, such as Adobe Photoshop, and decide to change - your design and export it again at some point. In that case, the margins - would probably stay the same, even if the positions of the fields on the - page would change. - \e Screen01 should not display visible changes in the \uicontrol {2D} view: - \image loginui3-base-state.jpg "UI with all the buttons and fields in the Design mode" + \image loginui4-base-state.webp "UI with all the buttons and fields" - \section2 Adding a Timeline and Animation Settings + \section2 Adding a Timeline You are now ready to add the \l{Creating Timeline Animations}{timeline}. @@ -122,24 +127,23 @@ \li Select \uicontrol View > \uicontrol Views > \uicontrol Timeline to open the \l Timeline view. \li In \uicontrol Timeline, select \inlineimage icons/plus.png - to add a 1000-frame timeline and settings for running the animation. - \image loginui4-timeline-settings.png + to add a 1000-frame timeline and define settings for running the animation. \li In the \uicontrol {Animation ID} field, enter \e toCreateAccountState. - \li Deselect the \uicontrol {Running in base state} check box because + \li Clear the \uicontrol {Running in base state} checkbox because you want the animation to run only after the user clicks the - \uicontrol {Create Account} button. You can use the default settings + \e {Create Account} button. Use the default settings for the other fields. \li Select \uicontrol Close in the \uicontrol {Timeline Settings} view to save the timeline and the animation settings. + \image loginui4-timeline-settings.png \endlist Next, you will record the animation in \uicontrol Timeline. \section2 Inserting Keyframes - You will now insert keyframes and record property changes in - \uicontrol Timeline: + To insert keyframes and record property changes in \uicontrol Timeline: \list 1 \li Select \e repeatPassword in \uicontrol Navigator to display its @@ -150,39 +154,41 @@ opacity property of the component. \image loginui4-keyframe-opacity.png "Inserting keyframe for opacity property" \li In \uicontrol Timeline, check that the playhead is in - frame 0, and select the \inlineimage icons/recordfill.png + frame \e 0, select the small arrow next to \e repeatPassword + keyframe to expand it, and then select the \inlineimage icons/recordfill.png (\uicontrol {Per Property Recording}) button for the \uicontrol opacity property of \e repeatPassword to start recording property changes. - \image loginui4-timeline-opacity.png "Record button for the opacity property" - \li In the field next to the opacity property name on that same line, - type 0 to hide the button, and press \key Enter to save the value. - \li Move the playhead to frame 1000 and change the opacity value to 1 + \li In \uicontrol Visibility > \uicontrol Opacity, type \e 0 to hide the button, and press + \key Enter to save the value. + \li Move the playhead to frame \e 1000 and change the opacity value to \e 1 to show the button. - To fine-tune the value of a keyframe, you can also right-click the + Alternatively, you can fine-tune the value of a keyframe by right-clicking the keyframe marker \inlineimage icons/keyframe_linear_active.png - , and select \uicontrol {Edit Keyframe}. + , and selecting \uicontrol {Edit Keyframe}. + \image loginui4-timeline.webp "Recording the opacity property" \li Select the record button again to stop recording property changes. If you forget this, all the following changes will be recorded, and the results will be unpredictable. \li Select \e createAccount in \uicontrol Navigator, and repeat the above steps to insert a keyframe for the \uicontrol Opacity property of the button and to record changes for it. However, this - time the opacity value needs to be 1 in frame 0 and 0 in frame 1000. + time the opacity value needs to be \e 1 in frame \e 0 and \e 0 in frame \e 1000. + \image loginui4-timeline-opacity.webp "Recorded timeline for the opacity properties" \li Select \uicontrol File > \uicontrol Save or press \key {Ctrl+S} to save your changes. \endlist - When you move the playhead along the timeline, you can see how the create - account button fades out while the repeat password field fades in. + When you move the playhead along the timeline, you can see how the \e {Create Account} + button fades out while the \e {Repeat Password} field fades in. - You will now animate the top anchor margin of the repeat password field - to make it appear to slide down from the password field. + You will now animate the top anchor margin of the \e {Repeat Password} field + to make it appear to slide down from the \e Password field. \section2 Animating Anchors - To animate the top anchor margin of the repeat password field: + To animate the top anchor margin of the \e {Repeat Password} field: \list 1 \li Select \e repeatPassword in \uicontrol Navigator to display its @@ -194,16 +200,17 @@ anchor margin of \e repeatPassword. \image loginui4-keyframe-top-anchor-margin.png "Inserting keyframe for top anchor margin" \li In \uicontrol Timeline, check that the playhead is in - frame 0, and select the record button for the \e anchors.topMargin + frame \e 0, and select the record button for the \e anchors.topMargin property of \e repeatPassword. - \li In the field next to the property, set a negative value for the - top anchor margin, -100, to place \e repeatPassword on top of - \e password. - \li Move the playhead to frame 1000 and change the top anchor margin - to 20, so that, combined with the change in the \uicontrol Opacity + \li In \uicontrol Layout > \uicontrol Margin (under \inlineimage icons/anchor-top.png), + set a negative value for the top anchor margin, \e -100, to place \e repeatPassword + on top of \e password. + \li Move the playhead to frame \e 1000 and change the top anchor margin + to \e 20, so that, combined with the change in the \uicontrol Opacity value, \e repeatPassword appears to slide down and settle below \e password. \li Select the record button again to stop recording property changes. + \image loginui4-timeline-all.webp "Recorded timeline for repeatPassword and createAccount." \li Select \uicontrol File > \uicontrol Save or press \key {Ctrl+S} to save your changes. \endlist @@ -215,9 +222,9 @@ \list 1 \li Click the keyframe marker \inlineimage icons/keyframe_linear_active.png - for the \e anchors.topMargin property at frame 1000 on the + for the \e anchors.topMargin property at frame \e 1000 on the timeline to select it. - \image loginui4-easing-curve-top-anchor-margin.png "Top anchor margin keyframe marker" + \image loginui4-easing-curve-top-anchor-margin.webp "Top anchor margin keyframe marker" \li Right-click the keyframe marker to open a context menu, and select \uicontrol {Edit Easing Curve} to add an easing curve to the animation. @@ -233,73 +240,46 @@ Your timeline should now look something like this: - \image loginui4-timeline.png "Timeline view with the recorded property changes" + \image loginui4-timeline-final.webp "Timeline view with the recorded property changes" - Next, you'll create states for the login and account creation pages and bind + Next, you will create states for the login and account creation pages and bind them to the animation settings. \section1 Binding Animation to States - You will now bring back the \l{Working with States}{states} in the + To bring back the \l{Working with States}{states} in the \uicontrol States view and bind them to the animation settings in \uicontrol Timeline: \list 1 \li In \uicontrol States, select \inlineimage icons/plus.png - twice to add two states called \e login and \e createAccount. You + twice to add two states and name them \e login and \e createAccount. You don't need to make any property changes this time because you'll bind the states to property animations. - \li In \uicontrol States, select \inlineimage icons/action-icon.png - for \e login to open the \uicontrol Actions menu, and then - select \uicontrol {Set as Default} to determine that the \e login - state is applied when the application starts. - \li In \uicontrol Timeline, select the \inlineimage icons/animation.png - (\uicontrol {Timeline Settings (S)}) button on the toolbar (or press - \key S) to open the \uicontrol {Timeline Settings} dialog. - \image loginui4-timeline-settings-states.png + \li In \uicontrol States, select \uicontrol Default for \e login to determine that + the \e login state is applied when the application starts. + \image loginui4-states.webp "Created states in the States view." + \li With the base state selected, select \uicontrol Timeline > + \inlineimage icons/settings.png (\uicontrol {Timeline Settings (S)}) on the toolbar + (or press \key S) to open the \uicontrol {Timeline Settings} dialog. + \li Double-click the cell in the \uicontrol Timeline column on the + \e login row, and select \e timeline in the list. \li Double-click the cell in the \uicontrol Timeline column on the \e createAccount row, and select \e timeline in the list. \li Double-click the cell in the \uicontrol Animation column on the \e createAccount row, and select \e toCreateAccountState. + \image loginui4-timeline-settings-states.png \li Click \uicontrol Close to save the timeline settings. \li Select \uicontrol File > \uicontrol Save or press \key {Ctrl+S} to save your changes. \endlist - In the live preview, you can now click the \uicontrol {Create Account} + In the live preview, you can now click the \e {Create Account} button to go to the account creation page. \image loginui4.gif "Moving between login page and account creation page" - \section2 Learn More - Timeline - - The Qt Quick Timeline module provides components to use timelines and - keyframes to animate component properties in UIs. Animating properties - enables their values to move through intermediate values instead of - immediately changing to the target value. - - The Keyframe component specifies the value of a keyframe on a timeline. \QDS - automatically adds keyframes between two keyframes, and sets their values - evenly to create an appearance of movement or transformation. - - An easing curve can be attached to the keyframe to change the appearance - of the animation. For more information about easing curve types, see the - documentation for \l [QML] {PropertyAnimation}{easing curves}. - - To be able to use the functionality of Timeline components, \QDS adds - the following \e import statement to the UI files where it uses the - components: - - \quotefromfile Loginui4/content/Screen01.ui.qml - \skipto QtQuick.Timeline - \printuntil 1.0 - - All the properties and functions of the components from this module are - available in the \uicontrol Design mode, and therefore it is enough to - learn how to use \uicontrol Timeline, as described in - \l {Creating Timeline Animations}. - \section1 Next Steps - For more examples about using timelines, see \l{Examples}. + To continue learning about \QDS, see \l{Examples} and other \l{Tutorials}. */ diff --git a/doc/qtdesignstudio/images/icons/more-button.png b/doc/qtdesignstudio/images/icons/more-button.png new file mode 100644 index 0000000000000000000000000000000000000000..0400e998a23ea57a8c713af25cc340b6bb4f4a1a GIT binary patch literal 1558 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!s7I14-?iy0WWg+Z8+Vb&Z8 z1_oA@%#etZ2wxwokg&dz0$52&wylyQ$U=n(-v9;Y{GwC^OFcu~WCH^u1#?TiG-Fef)U;$B1tS9^ zV|_zIeFIBfLnA8_11lqQ1t?ImQ?MyYNwW%aaf4b`l#*tvlu=SrV5P5LUS6(OZmgGI zl&)`RX=$l%V5Dzkq+67drdwQ@SCUwvn^&w1Gr=XbIJqdZpd>RtPXT0NVp4u-iLH_n z)YyvL0=Thx#n5m{&d=4aNG#Ad)H4A23GCUFWVpJ5(xM!&kGF7t6Oq&;Z_uvxR#aRS6v)ZS&*t9lvP}6`8oMT zzWFJswo1leSNQr`dFB86<*BX@FIS3*fj)3;Nw(Ff&Th!^$2c?HS$U^%3m4pScF;s#=ZOC?YVWv75B`cm^!Y?X?X z?CsVT>po{-V3^_Q;uvDl`}UG8A7h|M+r#u3VM7rK|^)+@7v}F6*2UngA(P^w0nR literal 0 HcmV?d00001 From 68663b312c8c09b544c3f8dcbb0a6810a766162d Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Mon, 4 Mar 2024 15:31:31 +0100 Subject: [PATCH 135/176] Core: Only show design themes in Qt Design Studio Task-number: QDS-12156 Change-Id: Ie2794104fa3f60537652d28f908cf5300540f530 Reviewed-by: Brook Cronin Reviewed-by: Tim Jenssen Reviewed-by: Reviewed-by: Qt CI Patch Build Bot --- src/plugins/coreplugin/themechooser.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/plugins/coreplugin/themechooser.cpp b/src/plugins/coreplugin/themechooser.cpp index e59c6d83a38..2903e573cac 100644 --- a/src/plugins/coreplugin/themechooser.cpp +++ b/src/plugins/coreplugin/themechooser.cpp @@ -176,7 +176,11 @@ static void addThemesFromPath(const QString &path, QList *themes) const QStringList themeList = themeDir.entryList(); for (const QString &fileName : std::as_const(themeList)) { QString id = QFileInfo(fileName).completeBaseName(); - themes->append(ThemeEntry(Id::fromString(id), themeDir.absoluteFilePath(fileName))); + bool addTheme = true; + if (Core::ICore::isQtDesignStudio()) + addTheme = id.startsWith("design"); + if (addTheme) + themes->append(ThemeEntry(Id::fromString(id), themeDir.absoluteFilePath(fileName))); } } From cf67c1aee66f4a2e2be9e6fb957de51392e38bcd Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Tue, 5 Mar 2024 17:14:33 +0100 Subject: [PATCH 136/176] QmlDesigner: Fix crash Task-number: QDS-12167 Change-Id: I413abcbedb37366fe433b8f30c0e596fa35de846 Reviewed-by: Reviewed-by: Tim Jenssen Reviewed-by: Qt CI Patch Build Bot --- .../components/connectioneditor/connectionmodel.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp index 8b649e5a9a4..d3fcd019fbd 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp @@ -365,7 +365,11 @@ void ConnectionModel::addConnection(const PropertyName &signalName) NodeMetaInfo nodeMetaInfo = connectionView()->model()->qtQuickConnectionsMetaInfo(); if (nodeMetaInfo.isValid()) { - ModelNode selectedNode = connectionView()->selectedModelNodes().constFirst(); + ModelNode selectedNode; + if (connectionView()->selectedModelNodes().isEmpty()) + selectedNode = connectionView()->rootModelNode(); + else + selectedNode = connectionView()->selectedModelNodes().constFirst(); PropertyName signalHandlerName = signalName; if (signalHandlerName.isEmpty()) From f6528007afcb1b7044e53824fca3651399cce27a Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Wed, 6 Mar 2024 12:59:50 +0100 Subject: [PATCH 137/176] QmlDesigner: Remove ContentNotEditableIndicator The indicator was used for tab views of controls 1. There is no current use case. We have seen crashes related to the indicator. Change-Id: I0466aecfd5ae598e07ebbfe8d1277ce23116e183 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Marco Bubke Reviewed-by: Tim Jenssen --- src/plugins/qmldesigner/CMakeLists.txt | 1 - .../contentnoteditableindicator.cpp | 102 ------------------ .../formeditor/contentnoteditableindicator.h | 36 ------- .../components/formeditor/movetool.cpp | 21 ++-- .../components/formeditor/movetool.h | 2 - .../components/formeditor/selectiontool.cpp | 5 - .../components/formeditor/selectiontool.h | 2 - 7 files changed, 8 insertions(+), 161 deletions(-) delete mode 100644 src/plugins/qmldesigner/components/formeditor/contentnoteditableindicator.cpp delete mode 100644 src/plugins/qmldesigner/components/formeditor/contentnoteditableindicator.h diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 66af859a61b..09d0aa01c09 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -667,7 +667,6 @@ extend_qtc_plugin(QmlDesigner backgroundaction.cpp backgroundaction.h bindingindicator.cpp bindingindicator.h bindingindicatorgraphicsitem.cpp bindingindicatorgraphicsitem.h - contentnoteditableindicator.cpp contentnoteditableindicator.h controlelement.cpp controlelement.h dragtool.cpp dragtool.h formeditor.qrc diff --git a/src/plugins/qmldesigner/components/formeditor/contentnoteditableindicator.cpp b/src/plugins/qmldesigner/components/formeditor/contentnoteditableindicator.cpp deleted file mode 100644 index 8c40c8c3583..00000000000 --- a/src/plugins/qmldesigner/components/formeditor/contentnoteditableindicator.cpp +++ /dev/null @@ -1,102 +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 - -#include "contentnoteditableindicator.h" -#include "nodemetainfo.h" - -#include - -#include -#include - -namespace QmlDesigner { - -ContentNotEditableIndicator::ContentNotEditableIndicator(LayerItem *layerItem) - : m_layerItem(layerItem) -{ - -} - -ContentNotEditableIndicator::ContentNotEditableIndicator() = default; - -ContentNotEditableIndicator::~ContentNotEditableIndicator() -{ - clear(); -} - -void ContentNotEditableIndicator::clear() -{ - for (const EntryPair &entryPair : std::as_const(m_entryList)) { - delete entryPair.second; - entryPair.first->blurContent(false); - } - - m_entryList.clear(); -} - -bool operator ==(const ContentNotEditableIndicator::EntryPair &firstPair, const ContentNotEditableIndicator::EntryPair &secondPair) -{ - return firstPair.first == secondPair.first; -} - -void ContentNotEditableIndicator::setItems(const QList &itemList) -{ - removeEntriesWhichAreNotInTheList(itemList); - addAddiationEntries(itemList); -} - -void ContentNotEditableIndicator::updateItems(const QList &itemList) -{ - QSet affectedFormEditorItemItems; - affectedFormEditorItemItems.unite(Utils::toSet(itemList)); - for (FormEditorItem *formEditorItem : itemList) - affectedFormEditorItemItems.unite(Utils::toSet(formEditorItem->offspringFormEditorItems())); - - for (const EntryPair &entryPair : std::as_const(m_entryList)) { - for (FormEditorItem *formEditorItem : std::as_const(affectedFormEditorItemItems)) { - if (formEditorItem == entryPair.first) { - QRectF boundingRectangleInSceneSpace - = formEditorItem->qmlItemNode().instanceSceneTransform().mapRect( - formEditorItem->qmlItemNode().instanceBoundingRect()); - entryPair.second->setRect(boundingRectangleInSceneSpace); - entryPair.second->update(); - } - } - } -} - -void ContentNotEditableIndicator::addAddiationEntries(const QList &itemList) -{ - for (FormEditorItem *formEditorItem : itemList) { - const ModelNode modelNode = formEditorItem->qmlItemNode().modelNode(); - if (modelNode.metaInfo().isValid() && modelNode.metaInfo().isQtQuickLoader()) { - if (!m_entryList.contains(EntryPair(formEditorItem, 0))) { - auto indicatorShape = new QGraphicsRectItem(m_layerItem); - QPen linePen; - linePen.setCosmetic(true); - linePen.setColor(QColor(0xa0, 0xa0, 0xa0)); - indicatorShape->setPen(linePen); - QRectF boundingRectangleInSceneSpace = formEditorItem->qmlItemNode().instanceSceneTransform().mapRect(formEditorItem->qmlItemNode().instanceBoundingRect()); - indicatorShape->setRect(boundingRectangleInSceneSpace); - static QBrush brush(QColor(0, 0, 0, 10), Qt::BDiagPattern); - indicatorShape->setBrush(brush); - - m_entryList.append(EntryPair(formEditorItem, indicatorShape)); - } - } - } -} - -void ContentNotEditableIndicator::removeEntriesWhichAreNotInTheList(const QList &itemList) -{ - for (int i = 0; i < m_entryList.size(); ++i) { - const EntryPair &entryPair = m_entryList.at(i); - if (!itemList.contains(entryPair.first)) { - delete entryPair.second; - entryPair.first->blurContent(false); - m_entryList.removeAt(i--); - } - } -} - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/formeditor/contentnoteditableindicator.h b/src/plugins/qmldesigner/components/formeditor/contentnoteditableindicator.h deleted file mode 100644 index 514331e2ac9..00000000000 --- a/src/plugins/qmldesigner/components/formeditor/contentnoteditableindicator.h +++ /dev/null @@ -1,36 +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 "layeritem.h" -#include "formeditoritem.h" - -#include -#include - -namespace QmlDesigner { - -class ContentNotEditableIndicator -{ -public: - using EntryPair = QPair; - - ContentNotEditableIndicator(LayerItem *layerItem); - ContentNotEditableIndicator(); - ~ContentNotEditableIndicator(); - - void clear(); - - void setItems(const QList &itemList); - void updateItems(const QList &itemList); - -protected: - void addAddiationEntries(const QList &itemList); - void removeEntriesWhichAreNotInTheList(const QList &itemList); - -private: - QPointer m_layerItem; - QList m_entryList; -}; - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/formeditor/movetool.cpp b/src/plugins/qmldesigner/components/formeditor/movetool.cpp index 0d486b0f4f9..7aa66c2390a 100644 --- a/src/plugins/qmldesigner/components/formeditor/movetool.cpp +++ b/src/plugins/qmldesigner/components/formeditor/movetool.cpp @@ -21,14 +21,14 @@ namespace QmlDesigner { MoveTool::MoveTool(FormEditorView *editorView) - : AbstractFormEditorTool(editorView), - m_moveManipulator(editorView->scene()->manipulatorLayerItem(), editorView), - m_selectionIndicator(editorView->scene()->manipulatorLayerItem()), - m_resizeIndicator(editorView->scene()->manipulatorLayerItem()), - m_rotationIndicator(editorView->scene()->manipulatorLayerItem()), - m_anchorIndicator(editorView->scene()->manipulatorLayerItem()), - m_bindingIndicator(editorView->scene()->manipulatorLayerItem()), - m_contentNotEditableIndicator(editorView->scene()->manipulatorLayerItem()) + : AbstractFormEditorTool(editorView) + , m_moveManipulator(editorView->scene()->manipulatorLayerItem(), editorView) + , m_selectionIndicator(editorView->scene()->manipulatorLayerItem()) + , m_resizeIndicator(editorView->scene()->manipulatorLayerItem()) + , m_rotationIndicator(editorView->scene()->manipulatorLayerItem()) + , m_anchorIndicator(editorView->scene()->manipulatorLayerItem()) + , m_bindingIndicator(editorView->scene()->manipulatorLayerItem()) + { m_selectionIndicator.setCursor(Qt::SizeAllCursor); } @@ -44,7 +44,6 @@ void MoveTool::clear() m_rotationIndicator.clear(); m_anchorIndicator.clear(); m_bindingIndicator.clear(); - m_contentNotEditableIndicator.clear(); AbstractFormEditorTool::clear(); if (view()->formEditorWidget()->graphicsView()) @@ -142,9 +141,6 @@ void MoveTool::hoverMoveEvent(const QList &itemList, view()->changeToSelectionTool(); return; } - - - m_contentNotEditableIndicator.setItems(toFormEditorItemList(itemList)); } void MoveTool::keyPressEvent(QKeyEvent *event) @@ -381,7 +377,6 @@ void MoveTool::formEditorItemsChanged(const QList &itemList) m_rotationIndicator.updateItems(selectedItemList); m_anchorIndicator.updateItems(selectedItemList); m_bindingIndicator.updateItems(selectedItemList); - m_contentNotEditableIndicator.updateItems(selectedItemList); } void MoveTool::focusLost() diff --git a/src/plugins/qmldesigner/components/formeditor/movetool.h b/src/plugins/qmldesigner/components/formeditor/movetool.h index e7176f32639..0370c9010db 100644 --- a/src/plugins/qmldesigner/components/formeditor/movetool.h +++ b/src/plugins/qmldesigner/components/formeditor/movetool.h @@ -9,7 +9,6 @@ #include "rotationindicator.h" #include "anchorindicator.h" #include "bindingindicator.h" -#include "contentnoteditableindicator.h" namespace QmlDesigner { @@ -66,7 +65,6 @@ private: RotationIndicator m_rotationIndicator; AnchorIndicator m_anchorIndicator; BindingIndicator m_bindingIndicator; - ContentNotEditableIndicator m_contentNotEditableIndicator; QList m_movingItems; }; diff --git a/src/plugins/qmldesigner/components/formeditor/selectiontool.cpp b/src/plugins/qmldesigner/components/formeditor/selectiontool.cpp index 282208f3b1a..d30539d52b6 100644 --- a/src/plugins/qmldesigner/components/formeditor/selectiontool.cpp +++ b/src/plugins/qmldesigner/components/formeditor/selectiontool.cpp @@ -31,7 +31,6 @@ SelectionTool::SelectionTool(FormEditorView *editorView) , m_rotationIndicator(editorView->scene()->manipulatorLayerItem()) , m_anchorIndicator(editorView->scene()->manipulatorLayerItem()) , m_bindingIndicator(editorView->scene()->manipulatorLayerItem()) - , m_contentNotEditableIndicator(editorView->scene()->manipulatorLayerItem()) { m_selectionIndicator.setCursor(Qt::ArrowCursor); } @@ -143,8 +142,6 @@ void SelectionTool::hoverMoveEvent(const QList &itemList, } scene()->highlightBoundingRect(topSelectableItem); - - m_contentNotEditableIndicator.setItems(toFormEditorItemList(itemList)); } void SelectionTool::mouseReleaseEvent(const QList &itemList, @@ -250,7 +247,6 @@ void SelectionTool::clear() m_rotationIndicator.clear(); m_anchorIndicator.clear(); m_bindingIndicator.clear(); - m_contentNotEditableIndicator.clear(); AbstractFormEditorTool::clear(); } @@ -273,7 +269,6 @@ void SelectionTool::formEditorItemsChanged(const QList &itemLis m_rotationIndicator.updateItems(selectedItemList); m_anchorIndicator.updateItems(selectedItemList); m_bindingIndicator.updateItems(selectedItemList); - m_contentNotEditableIndicator.updateItems(selectedItemList); } void SelectionTool::instancesCompleted(const QList &/*itemList*/) diff --git a/src/plugins/qmldesigner/components/formeditor/selectiontool.h b/src/plugins/qmldesigner/components/formeditor/selectiontool.h index 712de3ccf89..5453c43ac4c 100644 --- a/src/plugins/qmldesigner/components/formeditor/selectiontool.h +++ b/src/plugins/qmldesigner/components/formeditor/selectiontool.h @@ -10,7 +10,6 @@ #include "rotationindicator.h" #include "anchorindicator.h" #include "bindingindicator.h" -#include "contentnoteditableindicator.h" #include #include @@ -67,7 +66,6 @@ private: RotationIndicator m_rotationIndicator; AnchorIndicator m_anchorIndicator; BindingIndicator m_bindingIndicator; - ContentNotEditableIndicator m_contentNotEditableIndicator; QElapsedTimer m_mousePressTimer; QCursor m_cursor; bool m_itemSelectedAndMovable = false; From af9ce822459c798bc7a151c2b84ccf3d41dec433 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Mon, 4 Mar 2024 18:20:59 +0100 Subject: [PATCH 138/176] QmlDesigner: Fix legacy NodeMetaInfo::componentFileName() Instead of NodeMetaInfo::componentFileName() you should use NodeMetaInfo::sourceId() with the new code model. Change-Id: I22d14e53bd7a0a68425d8586c00d859f49d1d421 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Reviewed-by: Thomas Hartmann --- .../qmldesigner/designercore/metainfo/nodemetainfo.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp index 8ae3c42c137..bf9066b0b6a 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp @@ -2029,10 +2029,6 @@ QString NodeMetaInfo::componentFileName() const if (isValid()) { return m_privateData->componentFileName(); } - } else { - if (isValid()) { - return m_privateData->componentFileName(); - } } return {}; From 74761b0e64542b9b032d48fca8be132895acb743 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 1 Mar 2024 15:41:19 +0200 Subject: [PATCH 139/176] QmlDesigner: Add fly mode for 3D view You can now activate fly mode in 3D view by pressing right mouse button. In fly mode, cursor is hidden and mouse controls edit camera rotation directly, and WASDQE can be used to move the camera around. Fixes: QDS-12030 Change-Id: I52550502632af19de36a1557d9aac84ff3cb18cc Reviewed-by: Mahmoud Badri Reviewed-by: Qt CI Patch Build Bot --- .../interfaces/nodeinstanceglobal.h | 6 +- .../components/edit3d/edit3dcanvas.cpp | 96 ++++++++++++++- .../components/edit3d/edit3dcanvas.h | 19 ++- .../components/edit3d/edit3dview.cpp | 64 ++++++++-- .../components/edit3d/edit3dview.h | 9 +- .../components/edit3d/edit3dwidget.cpp | 1 + .../components/edit3d/edit3dwidget.h | 7 ++ .../mockfiles/qt6/EditCameraController.qml | 110 +++++++++++++----- .../qml2puppet/mockfiles/qt6/EditView3D.qml | 20 ++++ .../qml2puppet/editor3d/generalhelper.cpp | 106 ++++++++++++++++- .../qml2puppet/editor3d/generalhelper.h | 21 ++++ .../qt5informationnodeinstanceserver.cpp | 31 ++++- 12 files changed, 439 insertions(+), 51 deletions(-) diff --git a/src/libs/qmlpuppetcommunication/interfaces/nodeinstanceglobal.h b/src/libs/qmlpuppetcommunication/interfaces/nodeinstanceglobal.h index d5a715f1d86..56a2796de09 100644 --- a/src/libs/qmlpuppetcommunication/interfaces/nodeinstanceglobal.h +++ b/src/libs/qmlpuppetcommunication/interfaces/nodeinstanceglobal.h @@ -49,7 +49,11 @@ enum class View3DActionType { SetBakeLightsView3D, SplitViewToggle, MaterialOverride, - ShowWireframe + ShowWireframe, + FlyModeToggle, + EditCameraRotation, + EditCameraMove, + EditCameraStopAllMoves }; constexpr bool isNanotraceEnabled() diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.cpp index 6afe4fa8164..2e8ef8304f6 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.cpp @@ -17,6 +17,8 @@ #include #include +#include +#include #include #include #include @@ -77,10 +79,67 @@ QWidget *Edit3DCanvas::busyIndicator() const return m_busyIndicator; } +void Edit3DCanvas::setFlyMode(bool enabled, const QPoint &pos) +{ + if (m_flyMode == enabled) + return; + + m_flyMode = enabled; + + if (enabled) { + m_flyModeStartTime = QDateTime::currentMSecsSinceEpoch(); + + // Mouse cursor will be hidden in the flight mode + QApplication::setOverrideCursor(QCursor(Qt::BlankCursor)); + + m_flyModeStartCursorPos = pos; + m_flyModeFirstUpdate = true; + + // Hide cursor on the middle of the active split to make the wheel work during flight mode. + // We can't rely on current activeSplit value, as mouse press to enter flight mode can change the + // active split, so hide the cursor based on its current location. + QPoint center = mapToGlobal(QPoint(width() / 2, height() / 2)); + if (m_parent->view()->isSplitView()) { + if (pos.x() <= center.x()) { + if (pos.y() <= center.y()) + m_hiddenCursorPos = mapToGlobal(QPoint(width() / 4, height() / 4)); + else + m_hiddenCursorPos = mapToGlobal(QPoint(width() / 4, (height() / 4) * 3)); + } else { + if (pos.y() <= center.y()) + m_hiddenCursorPos = mapToGlobal(QPoint((width() / 4) * 3, height() / 4)); + else + m_hiddenCursorPos = mapToGlobal(QPoint((width() / 4) * 3, (height() / 4) * 3)); + } + } else { + m_hiddenCursorPos = center; + } + + QCursor::setPos(m_hiddenCursorPos); + } else { + QCursor::setPos(m_flyModeStartCursorPos); + + if (QApplication::overrideCursor()) + QApplication::restoreOverrideCursor(); + + if (m_contextMenuPending && (QDateTime::currentMSecsSinceEpoch() - m_flyModeStartTime) < 500) + m_parent->view()->showContextMenu(); + + m_contextMenuPending = false; + m_flyModeStartTime = 0; + } + + m_parent->view()->setFlyMode(enabled); +} + void Edit3DCanvas::mousePressEvent(QMouseEvent *e) { - if (e->button() == Qt::RightButton && e->modifiers() == Qt::NoModifier) + m_contextMenuPending = false; + if (!m_flyMode && e->modifiers() == Qt::NoModifier && e->buttons() == Qt::RightButton) { + setFlyMode(true, e->globalPos()); m_parent->view()->startContextMenu(e->pos()); + m_contextMenuPending = true; + } m_parent->view()->sendInputEvent(e); QWidget::mousePressEvent(e); @@ -88,6 +147,8 @@ void Edit3DCanvas::mousePressEvent(QMouseEvent *e) void Edit3DCanvas::mouseReleaseEvent(QMouseEvent *e) { + if ((e->buttons() & Qt::RightButton) == Qt::NoButton) + setFlyMode(false); m_parent->view()->sendInputEvent(e); QWidget::mouseReleaseEvent(e); } @@ -100,8 +161,29 @@ void Edit3DCanvas::mouseDoubleClickEvent(QMouseEvent *e) void Edit3DCanvas::mouseMoveEvent(QMouseEvent *e) { - m_parent->view()->sendInputEvent(e); + if (!m_flyMode) + m_parent->view()->sendInputEvent(e); + QWidget::mouseMoveEvent(e); + + if (m_flyMode && e->globalPos() != m_hiddenCursorPos) { + if (!m_flyModeFirstUpdate) { + // We notify explicit camera rotation need for puppet rather than rely in mouse events, + // as mouse isn't grabbed on puppet side and can't handle fast movements that go out of + // edit camera mouse area. This also simplifies split view handling. + QPointF diff = m_hiddenCursorPos - e->globalPos(); + if (e->buttons() == (Qt::LeftButton | Qt::RightButton)) { + m_parent->view()->emitView3DAction(View3DActionType::EditCameraMove, + QVector3D{float(-diff.x()), float(-diff.y()), 0.f}); + } else { + m_parent->view()->emitView3DAction(View3DActionType::EditCameraRotation, diff / 6.); + } + } else { + // Skip first move to avoid undesirable jump occasionally when initiating flight mode + m_flyModeFirstUpdate = false; + } + QCursor::setPos(m_hiddenCursorPos); + } } void Edit3DCanvas::wheelEvent(QWheelEvent *e) @@ -112,13 +194,15 @@ void Edit3DCanvas::wheelEvent(QWheelEvent *e) void Edit3DCanvas::keyPressEvent(QKeyEvent *e) { - m_parent->view()->sendInputEvent(e); + if (!e->isAutoRepeat()) + m_parent->view()->sendInputEvent(e); QWidget::keyPressEvent(e); } void Edit3DCanvas::keyReleaseEvent(QKeyEvent *e) { - m_parent->view()->sendInputEvent(e); + if (!e->isAutoRepeat()) + m_parent->view()->sendInputEvent(e); QWidget::keyReleaseEvent(e); } @@ -146,6 +230,10 @@ void Edit3DCanvas::focusOutEvent(QFocusEvent *focusEvent) { QmlDesignerPlugin::emitUsageStatisticsTime(Constants::EVENT_3DEDITOR_TIME, m_usageTimer.elapsed()); + + setFlyMode(false); + m_parent->view()->emitView3DAction(View3DActionType::EditCameraStopAllMoves, {}); + QWidget::focusOutEvent(focusEvent); } diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.h b/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.h index 810e246f8a3..39207554a73 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.h +++ b/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.h @@ -2,11 +2,12 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #pragma once -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include namespace QmlDesigner { @@ -25,6 +26,8 @@ public: QImage renderImage() const; void setOpacity(qreal opacity); QWidget *busyIndicator() const; + void setFlyMode(bool enabled, const QPoint &pos = {}); + bool isFlyMode() const { return m_flyMode; } protected: void mousePressEvent(QMouseEvent *e) override; @@ -50,6 +53,12 @@ private: QElapsedTimer m_usageTimer; qreal m_opacity = 1.0; QWidget *m_busyIndicator = nullptr; + bool m_flyMode = false; + QPoint m_flyModeStartCursorPos; + QPoint m_hiddenCursorPos; + qint64 m_flyModeStartTime = 0; + bool m_flyModeFirstUpdate = false; + bool m_contextMenuPending = false; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp index adea6f4b8d6..99bf125c982 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp @@ -449,11 +449,13 @@ void Edit3DView::customNotification([[maybe_unused]] const AbstractView *view, void Edit3DView::nodeAtPosReady(const ModelNode &modelNode, const QVector3D &pos3d) { if (m_nodeAtPosReqType == NodeAtPosReqType::ContextMenu) { - // Make sure right-clicked item is selected. Due to a bug in puppet side right-clicking an item - // while the context-menu is shown doesn't select the item. - if (modelNode.isValid() && !modelNode.isSelected()) - setSelectedModelNode(modelNode); - m_edit3DWidget->showContextMenu(m_contextMenuPos, modelNode, pos3d); + m_contextMenuPos3D = pos3d; + if (m_edit3DWidget->canvas()->isFlyMode()) { + m_contextMenuPendingNode = modelNode; + } else { + m_nodeAtPosReqType = NodeAtPosReqType::None; + showContextMenu(); + } } else if (m_nodeAtPosReqType == NodeAtPosReqType::ComponentDrop) { ModelNode createdNode; executeInTransaction(__FUNCTION__, [&] { @@ -679,6 +681,49 @@ QPoint Edit3DView::resolveToolbarPopupPos(Edit3DAction *action) const return pos; } +void Edit3DView::showContextMenu() +{ + // If request for context menu is still pending, skip for now + if (m_nodeAtPosReqType == NodeAtPosReqType::ContextMenu) + return; + + if (m_contextMenuPendingNode.isValid() && !m_contextMenuPendingNode.isSelected()) + setSelectedModelNode(m_contextMenuPendingNode); + m_edit3DWidget->showContextMenu(m_contextMenuPosMouse, m_contextMenuPendingNode, m_contextMenuPos3D); + m_contextMenuPendingNode = {}; +} + +void Edit3DView::setFlyMode(bool enabled) +{ + emitView3DAction(View3DActionType::FlyModeToggle, enabled); + + // Disable any actions with conflicting hotkeys + if (enabled) { + m_flyModeDisabledActions.clear(); + const QList controlKeys = { Qt::Key_W, Qt::Key_A, Qt::Key_S, + Qt::Key_D, Qt::Key_Q, Qt::Key_E, + Qt::Key_Up, Qt::Key_Down, Qt::Key_Left, + Qt::Key_Right, Qt::Key_PageDown, Qt::Key_PageUp}; + for (auto i = m_edit3DActions.cbegin(), end = m_edit3DActions.cend(); i != end; ++i) { + for (const QKeySequence &controlKey : controlKeys) { + if (Core::Command *cmd = m_edit3DWidget->actionToCommandHash().value(i.value()->action())) { + if (cmd->keySequence().matches(controlKey) == QKeySequence::ExactMatch) { + if (i.value()->action()->isEnabled()) { + m_flyModeDisabledActions.append(i.value()); + i.value()->action()->setEnabled(false); + } + break; + } + } + } + } + } else { + for (Edit3DAction *action : std::as_const(m_flyModeDisabledActions)) + action->action()->setEnabled(true); + m_flyModeDisabledActions.clear(); + } +} + void Edit3DView::syncSnapAuxPropsToSettings() { if (!model()) @@ -722,6 +767,11 @@ int Edit3DView::activeSplit() const return m_activeSplit; } +bool Edit3DView::isSplitView() const +{ + return m_splitViewAction->action()->isChecked(); +} + void Edit3DView::createEdit3DActions() { m_selectionModeAction = std::make_unique( @@ -850,7 +900,7 @@ void Edit3DView::createEdit3DActions() QmlDesigner::Constants::EDIT3D_EDIT_SHOW_SELECTION_BOX, View3DActionType::ShowSelectionBox, QCoreApplication::translate("ShowSelectionBoxAction", "Show Selection Boxes"), - QKeySequence(Qt::Key_S), + QKeySequence(Qt::Key_B), true, true, QIcon(), @@ -1189,7 +1239,7 @@ void Edit3DView::addQuick3DImport() // context menu is created when nodeAtPosReady() is received from puppet void Edit3DView::startContextMenu(const QPoint &pos) { - m_contextMenuPos = pos; + m_contextMenuPosMouse = pos; m_nodeAtPosReqType = NodeAtPosReqType::ContextMenu; } diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.h b/src/plugins/qmldesigner/components/edit3d/edit3dview.h index 5fc0d2961c9..88cac111a7a 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.h +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.h @@ -15,6 +15,7 @@ #include #include #include +#include QT_BEGIN_NAMESPACE class QAction; @@ -73,6 +74,7 @@ public: void addQuick3DImport(); void startContextMenu(const QPoint &pos); + void showContextMenu(); void dropMaterial(const ModelNode &matNode, const QPointF &pos); void dropBundleMaterial(const QPointF &pos); void dropBundleEffect(const QPointF &pos); @@ -88,6 +90,8 @@ public: void setSplitToolState(int splitIndex, const SplitToolState &state); int activeSplit() const; + bool isSplitView() const; + void setFlyMode(bool enabled); private slots: void onEntriesChanged(); @@ -167,7 +171,8 @@ private: ItemLibraryEntry m_droppedEntry; QString m_droppedFile; NodeAtPosReqType m_nodeAtPosReqType; - QPoint m_contextMenuPos; + QPoint m_contextMenuPosMouse; + QVector3D m_contextMenuPos3D; QTimer m_compressionTimer; QPointer m_bakeLights; bool m_isBakingLightsSupported = false; @@ -175,6 +180,8 @@ private: int m_activeSplit = 0; QList m_splitToolStates; + QList m_flyModeDisabledActions; + ModelNode m_contextMenuPendingNode; friend class Edit3DAction; }; diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp index dc57d75f7a6..07102ae8936 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp @@ -122,6 +122,7 @@ Edit3DWidget::Edit3DWidget(Edit3DView *view) // Register action as creator command to make it configurable Core::Command *command = Core::ActionManager::registerAction( a, action->menuId().constData(), context); + m_actionToCommandHash.insert(a, command); command->setDefaultKeySequence(a->shortcut()); if (proxyGroup) proxyGroup->addAction(command->action()); diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h index 0c9c807473b..211b044d41d 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h +++ b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h @@ -12,6 +12,10 @@ #include #include +namespace Core { +class Command; +} + namespace QmlDesigner { class Edit3DView; @@ -52,6 +56,8 @@ public: void showContextMenu(const QPoint &pos, const ModelNode &modelNode, const QVector3D &pos3d); void updateCreateSubMenu(const QList &entriesList); + const QHash &actionToCommandHash() { return m_actionToCommandHash; } + private slots: void onCreateAction(QAction *action); void onMatOverrideAction(QAction *action); @@ -100,6 +106,7 @@ private: QVector3D m_contextMenuPos3d; QHash m_nameToEntry; ItemLibraryEntry m_draggedEntry; + QHash m_actionToCommandHash; }; } // namespace QmlDesigner diff --git a/src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml b/src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml index cb9276305a3..e2321dbc3ac 100644 --- a/src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml +++ b/src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml @@ -26,8 +26,9 @@ Item { readonly property vector3d _defaultCameraPosition: Qt.vector3d(0, 600, 600) readonly property vector3d _defaultCameraRotation: Qt.vector3d(-45, 0, 0) readonly property real _defaultCameraLookAtDistance: _defaultCameraPosition.length() - readonly property real _keyPanAmount: 5 + readonly property real _keyPanAmount: 10 property bool ignoreToolState: false + property bool flyMode: viewRoot.flyMode z: 10 anchors.fill: parent @@ -150,6 +151,58 @@ Item { _lookAtPoint, _zoomFactor, true); } + function rotateCamera(angles) + { + cameraCtrl._lookAtPoint = _generalHelper.rotateCamera(camera, angles, _lookAtPoint); + } + + function moveCamera(moveVec) + { + cameraCtrl._lookAtPoint = _generalHelper.moveCamera(camera, _lookAtPoint, _zoomFactor, + moveVec); + } + + function getMoveVectorForKey(key) { + if (flyMode) { + switch (key) { + case Qt.Key_A: + case Qt.Key_Left: + return Qt.vector3d(_keyPanAmount, 0, 0); + case Qt.Key_D: + case Qt.Key_Right: + return Qt.vector3d(-_keyPanAmount, 0, 0); + case Qt.Key_E: + case Qt.Key_PageUp: + return Qt.vector3d(0, _keyPanAmount, 0); + case Qt.Key_Q: + case Qt.Key_PageDown: + return Qt.vector3d(0, -_keyPanAmount, 0); + case Qt.Key_W: + case Qt.Key_Up: + return Qt.vector3d(0, 0, _keyPanAmount); + case Qt.Key_S: + case Qt.Key_Down: + return Qt.vector3d(0, 0, -_keyPanAmount); + default: + break; + } + } else { + switch (key) { + case Qt.Key_Left: + return Qt.vector3d(_keyPanAmount, 0, 0); + case Qt.Key_Right: + return Qt.vector3d(-_keyPanAmount, 0, 0); + case Qt.Key_Up: + return Qt.vector3d(0, _keyPanAmount, 0); + case Qt.Key_Down: + return Qt.vector3d(0, -_keyPanAmount, 0); + default: + break; + } + } + return Qt.vector3d(0, 0, 0); + } + onCameraChanged: { if (camera && _prevCamera) { // Reset zoom on previous camera to ensure it's properties are good to copy to new cam @@ -166,6 +219,25 @@ Item { _prevCamera = camera; } + onFlyModeChanged: { + if (cameraCtrl._dragging) { + cameraCtrl._dragging = false; + cameraCtrl.storeCameraState(0); + } + _generalHelper.stopAllCameraMoves() + } + + Connections { + target: _generalHelper + enabled: viewRoot.activeSplit === cameraCtrl.splitId + function onRequestCameraMove(camera, moveVec) { + if (camera === cameraCtrl.camera) { + cameraCtrl.moveCamera(moveVec); + _generalHelper.requestRender(); + } + } + } + MouseArea { id: mouseHandler acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton @@ -190,6 +262,8 @@ Item { } } onPressed: (mouse) => { + if (cameraCtrl.flyMode) + return; viewRoot.activeSplit = cameraCtrl.splitId if (cameraCtrl.camera && mouse.modifiers === Qt.AltModifier) { cameraCtrl._dragging = true; @@ -215,6 +289,8 @@ Item { onCanceled: handleRelease() onWheel: (wheel) => { + if (cameraCtrl.flyMode && cameraCtrl.splitId !== viewRoot.activeSplit) + return; viewRoot.activeSplit = cameraCtrl.splitId if (cameraCtrl.camera) { // Empirically determined divisor for nice zoom @@ -225,33 +301,13 @@ Item { } Keys.onPressed: (event) => { - var pressPoint = Qt.vector3d(view3d.width / 2, view3d.height / 2, 0); - var currentPoint; + event.accepted = true; + _generalHelper.startCameraMove(cameraCtrl.camera, cameraCtrl.getMoveVectorForKey(event.key)); + } - switch (event.key) { - case Qt.Key_Left: - currentPoint = pressPoint.plus(Qt.vector3d(_keyPanAmount, 0, 0)); - break; - case Qt.Key_Right: - currentPoint = pressPoint.plus(Qt.vector3d(-_keyPanAmount, 0, 0)); - break; - case Qt.Key_Up: - currentPoint = pressPoint.plus(Qt.vector3d(0, _keyPanAmount, 0)); - break; - case Qt.Key_Down: - currentPoint = pressPoint.plus(Qt.vector3d(0, -_keyPanAmount, 0)); - break; - default: - break; - } - - if (currentPoint) { - _lookAtPoint = _generalHelper.panCamera( - camera, cameraCtrl.camera.sceneTransform, - cameraCtrl.camera.position, _lookAtPoint, - pressPoint, currentPoint, _zoomFactor); - event.accepted = true; - } + Keys.onReleased: (event) => { + event.accepted = true; + _generalHelper.stopCameraMove(cameraCtrl.getMoveVectorForKey(event.key)); } OriginGizmo { diff --git a/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml b/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml index 034615d27f4..23fc1c6a78e 100644 --- a/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml +++ b/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml @@ -37,6 +37,7 @@ Item { property color gridColor: "#cccccc" property bool syncEnvBackground: false property bool splitView: false + property bool flyMode: false enum SelectionMode { Item, Group } enum TransformMode { Move, Rotate, Scale } @@ -129,6 +130,7 @@ Item { selectionBoxCount = 0; editViewsChanged(); + cameraControls[activeSplit].forceActiveFocus(); return true; } return false; @@ -351,6 +353,11 @@ Item { cameraControls[i].restoreDefaultState(); } + if ("flyMode" in toolStates) + flyMode = toolStates.flyMode; + else if (resetToDefault) + flyMode = false; + if ("splitView" in toolStates) splitView = toolStates.splitView; else if (resetToDefault) @@ -598,6 +605,16 @@ Item { return activeOverlayView.gizmoAt(splitPoint.x, splitPoint.y); } + function rotateEditCamera(angles) + { + cameraControls[activeSplit].rotateCamera(angles); + } + + function moveEditCamera(amounts) + { + cameraControls[activeSplit].moveCamera(amounts); + } + Component.onCompleted: { createEditViews(); selectObjects([]); @@ -822,6 +839,9 @@ Item { property bool initialMoveBlock: false onPressed: (mouse) => { + if (viewRoot.flyMode) + return; + viewRoot.updateActiveSplit(mouse.x, mouse.y); let splitPoint = viewRoot.resolveSplitPoint(mouse.x, mouse.y); diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp index ecb71ce3838..52070332b46 100644 --- a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp +++ b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp @@ -66,6 +66,11 @@ GeneralHelper::GeneralHelper() QList defaultBg; defaultBg.append(QColor()); m_bgColor = QVariant::fromValue(defaultBg); + + m_camMoveData.timer.setInterval(16); + QObject::connect(&m_camMoveData.timer, &QTimer::timeout, this, [this]() { + emit requestCameraMove(m_camMoveData.camera, m_camMoveData.combinedMoveVector); + }); } void GeneralHelper::requestOverlayUpdate() @@ -142,14 +147,111 @@ QVector3D GeneralHelper::panCamera(QQuick3DCamera *camera, const QMatrix4x4 star const float *dataPtr(startTransform.data()); const QVector3D xAxis = QVector3D(dataPtr[0], dataPtr[1], dataPtr[2]).normalized(); const QVector3D yAxis = QVector3D(dataPtr[4], dataPtr[5], dataPtr[6]).normalized(); - const QVector3D xDelta = -1.f * xAxis * dragVector.x(); + const QVector3D xDelta = xAxis * dragVector.x(); const QVector3D yDelta = yAxis * dragVector.y(); - const QVector3D delta = (xDelta + yDelta) * zoomFactor; + const QVector3D delta = (yDelta - xDelta) * zoomFactor; camera->setPosition(startPosition + delta); return startLookAt + delta; } +// Moves camera in 3D space and returns new look-at point +QVector3D GeneralHelper::moveCamera(QQuick3DCamera *camera, const QVector3D &startLookAt, + float zoomFactor, const QVector3D &moveVector) +{ + + if (moveVector.length() < 0.001f) + return startLookAt; + + QMatrix4x4 m = camera->sceneTransform(); // Works because edit camera is at scene root + const float *dataPtr(m.data()); + const QVector3D xAxis = QVector3D(dataPtr[0], dataPtr[1], dataPtr[2]).normalized(); + const QVector3D yAxis = QVector3D(dataPtr[4], dataPtr[5], dataPtr[6]).normalized(); + const QVector3D zAxis = QVector3D(dataPtr[8], dataPtr[9], dataPtr[10]).normalized(); + 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; + + camera->setPosition(camera->position() + delta); + + return startLookAt + delta; +} + +// Rotates camera and returns the new look-at point +QVector3D GeneralHelper::rotateCamera(QQuick3DCamera *camera, const QPointF &angles, + const QVector3D &lookAtPoint) +{ + float lookAtDist = (camera->scenePosition() - lookAtPoint).length(); + + 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); + + QMatrix4x4 m = camera->sceneTransform(); + const float *dataPtr(m.data()); + QVector3D newLookVector(dataPtr[8], dataPtr[9], dataPtr[10]); + + newLookVector.normalize(); + newLookVector *= lookAtDist; + + return camera->scenePosition() - newLookVector; +} + +void GeneralHelper::updateCombinedCameraMoveVector() +{ + QVector3D combinedVec; + for (const QVector3D &vec : std::as_const(m_camMoveData.moveVectors)) + combinedVec += vec; + m_camMoveData.combinedMoveVector = combinedVec; +} + +// Key events can be buffered and there are repeat delays imposed by OS, so to get smooth camera +// movement in response to keys, register start/stop of moves along each axis and use timer to +// trigger new moves along registered axes. +void GeneralHelper::startCameraMove(QQuick3DCamera *camera, const QVector3D moveVector) +{ + if (moveVector.isNull()) + return; + + if (m_camMoveData.camera != camera) { + m_camMoveData.camera = camera; + m_camMoveData.moveVectors.clear(); + } + + if (!m_camMoveData.moveVectors.contains(moveVector)) { + m_camMoveData.moveVectors.append(moveVector); + updateCombinedCameraMoveVector(); + } + + if (!m_camMoveData.timer.isActive()) { + m_camMoveData.timer.start(); + emit requestCameraMove(camera, m_camMoveData.combinedMoveVector); + } +} + +void GeneralHelper::stopCameraMove(const QVector3D moveVector) +{ + if (moveVector.isNull()) + return; + + m_camMoveData.moveVectors.removeOne(moveVector); + + updateCombinedCameraMoveVector(); + + if (m_camMoveData.moveVectors.isEmpty()) + m_camMoveData.timer.stop(); +} + +void GeneralHelper::stopAllCameraMoves() +{ + m_camMoveData.moveVectors.clear(); + m_camMoveData.combinedMoveVector = {}; + m_camMoveData.timer.stop(); +} + float GeneralHelper::zoomCamera([[maybe_unused]] QQuick3DViewport *viewPort, QQuick3DCamera *camera, float distance, diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h index fd76cadcfc6..9afaf077c9b 100644 --- a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h +++ b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -52,6 +53,15 @@ public: const QVector3D &startPosition, const QVector3D &startLookAt, const QVector3D &pressPos, const QVector3D ¤tPos, float zoomFactor); + Q_INVOKABLE QVector3D moveCamera(QQuick3DCamera *camera,const QVector3D &startLookAt, + float zoomFactor, const QVector3D &moveVector); + Q_INVOKABLE QVector3D rotateCamera(QQuick3DCamera *camera, const QPointF &angles, + const QVector3D &lookAtPoint); + + Q_INVOKABLE void startCameraMove(QQuick3DCamera *camera, const QVector3D moveVector); + Q_INVOKABLE void stopCameraMove(const QVector3D moveVector); + Q_INVOKABLE void stopAllCameraMoves(); + Q_INVOKABLE float zoomCamera(QQuick3DViewport *viewPort, QQuick3DCamera *camera, float distance, float defaultLookAtDistance, const QVector3D &lookAt, float zoomFactor, bool relative); @@ -149,6 +159,8 @@ signals: void minGridStepChanged(); void updateDragTooltip(); void sceneEnvDataChanged(); + void requestCameraMove(QQuick3DCamera *camera, const QVector3D &moveVector); + void requestRender(); private: void handlePendingToolStateUpdate(); @@ -163,6 +175,15 @@ private: QHash m_toolStates; QHash m_toolStatesPending; QSet m_rotationBlockedNodes; + void updateCombinedCameraMoveVector(); + + struct CameraMoveKeyData { + QQuick3DCamera *camera; + QList moveVectors; + QVector3D combinedMoveVector; + QTimer timer; + }; + CameraMoveKeyData m_camMoveData; struct SceneEnvData { QQuick3DSceneEnvironment::QQuick3DEnvironmentBackgroundTypes backgroundMode; diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp index 5257b0214da..26c5fb9c702 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp @@ -254,8 +254,11 @@ void Qt5InformationNodeInstanceServer::handleInputEvents() QGuiApplication::sendEvent(m_editView3DData.window, &me); // Context menu requested - if (command.button() == Qt::RightButton && command.modifiers() == Qt::NoModifier) + if (command.type() == QEvent::MouseButtonPress + && command.buttons() == Qt::RightButton + && command.modifiers() == Qt::NoModifier) { getNodeAtPos(command.pos()); + } } } @@ -488,6 +491,9 @@ void Qt5InformationNodeInstanceServer::createEditView3D() auto helper = new QmlDesigner::Internal::GeneralHelper(); QObject::connect(helper, &QmlDesigner::Internal::GeneralHelper::toolStateChanged, this, &Qt5InformationNodeInstanceServer::handleToolStateChanged); + QObject::connect(helper, &QmlDesigner::Internal::GeneralHelper::requestRender, this, [this]() { + render3DEditView(1); + }); engine()->rootContext()->setContextProperty("_generalHelper", helper); engine()->addImageProvider(QLatin1String("IconGizmoImageProvider"), new QmlDesigner::Internal::IconGizmoImageProvider); @@ -2417,6 +2423,7 @@ void Qt5InformationNodeInstanceServer::view3DAction(const View3DActionCommand &c if (!m_editView3DSetupDone) return; +#ifdef QUICK3D_MODULE QVariantMap updatedToolState; QVariantMap updatedViewState; int renderCount = 1; @@ -2498,16 +2505,31 @@ void Qt5InformationNodeInstanceServer::view3DAction(const View3DActionCommand &c case View3DActionType::ParticlesSeek: m_particleAnimationDriver->setSeekerPosition(command.position()); break; -#endif -#ifdef QUICK3D_MODULE +#endif // QUICK3D_PARTICLES_MODULE case View3DActionType::GetNodeAtPos: { getNodeAtPos(command.value().toPointF()); return; } -#endif case View3DActionType::SplitViewToggle: updatedToolState.insert("splitView", command.isEnabled()); break; + case View3DActionType::FlyModeToggle: + updatedToolState.insert("flyMode", command.isEnabled()); + break; + case View3DActionType::EditCameraRotation: + QMetaObject::invokeMethod(m_editView3DData.rootItem, "rotateEditCamera", + Q_ARG(QVariant, command.value())); + break; + case View3DActionType::EditCameraMove: + QMetaObject::invokeMethod(m_editView3DData.rootItem, "moveEditCamera", + Q_ARG(QVariant, command.value())); + break; + case View3DActionType::EditCameraStopAllMoves: { + auto helper = qobject_cast(m_3dHelper); + if (helper) + emit helper->stopAllCameraMoves(); + break; + } case View3DActionType::ShowWireframe: updatedToolState.insert("showWireframe", command.value().toList()); break; @@ -2531,6 +2553,7 @@ void Qt5InformationNodeInstanceServer::view3DAction(const View3DActionCommand &c } render3DEditView(renderCount); +#endif // QUICK3D_MODULE } void Qt5InformationNodeInstanceServer::requestModelNodePreviewImage(const RequestModelNodePreviewImageCommand &command) From 47dd92573086f26899db2705c1b69471d58bd6b0 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Mon, 4 Mar 2024 15:42:07 +0200 Subject: [PATCH 140/176] QmlDesigner: Add HelperWidgets FlagsComboBox Change-Id: I05294aa1483926d943f8a40d1d9702298110fe94 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Miikka Heikkinen Reviewed-by: --- .../imports/HelperWidgets/FlagsComboBox.qml | 87 +++++++++++++++++++ .../imports/HelperWidgets/qmldir | 1 + .../imports/StudioControls/CustomComboBox.qml | 19 ++-- .../materialeditorcontextobject.cpp | 31 ++++--- .../materialeditorcontextobject.h | 20 +++-- .../materialeditorqmlbackend.cpp | 20 ++--- .../materialeditor/materialeditorqmlbackend.h | 6 +- .../designercore/metainfo/nodemetainfo.cpp | 6 +- 8 files changed, 147 insertions(+), 43 deletions(-) create mode 100644 share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FlagsComboBox.qml diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FlagsComboBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FlagsComboBox.qml new file mode 100644 index 00000000000..d6d52d8d16c --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FlagsComboBox.qml @@ -0,0 +1,87 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQuick +import StudioControls 1.0 as StudioControls +import StudioTheme 1.0 as StudioTheme + +/* + A ComboBox of flags, it expects a list mode as below, first item will act as clear: + +itemsModel: ListModel { + ListElement { + name: qsTr("Clear") + flag: 0 + } + ListElement { + name: "..." + flag: ... + } + ListElement { + name: "..." + flag: ... + } + ... +*/ +StudioControls.CustomComboBox { + id: root + + property variant backendValue + + property bool showExtendedFunctionButton: true + + Connections { + target: backendValue + + function onValueChangedQml() { + let numSelected = 0 + let selectedItem = "" + + for (let i = 1; i < root.itemsModel.count; ++i) { + let flag = (root.backendValue.value >> (i - 1)) & 1; + root.itemAt(i).checked = flag + + if (flag) { + selectedItem = root.itemsModel.get(i).name + ++numSelected + } + } + + // update ComboBox text + root.model = numSelected == 0 ? [qsTr("empty")] + : numSelected == 1 ? root.model = [selectedItem] + : [qsTr("%1 items selected").arg(numSelected)] + } + } + + model: [qsTr("empty")] + + itemDelegate: StudioControls.CheckBox { + text: name + actionIndicatorVisible: false + + onToggled: { + if (index === 0) { // Clear + root.itemAt(0).checked = false + root.backendValue.value = 0 + } else { + if (root.itemAt(index).checked) + root.backendValue.value |= flag + else + root.backendValue.value &= ~flag + } + } + } + + ExtendedFunctionLogic { + id: extFuncLogic + backendValue: root.backendValue + } + + actionIndicator.icon.color: extFuncLogic.color + actionIndicator.icon.text: extFuncLogic.glyph + actionIndicator.onClicked: extFuncLogic.show() + actionIndicator.forceVisible: extFuncLogic.menuVisible + + actionIndicator.visible: root.showExtendedFunctionButton +} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/qmldir b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/qmldir index 94c3da27186..ddade5606a1 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/qmldir +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/qmldir @@ -23,6 +23,7 @@ EditableListView 2.0 EditableListView.qml ExpandingSpacer 2.0 ExpandingSpacer.qml ExpressionTextField 2.0 ExpressionTextField.qml ExtendedFunctionLogic 2.0 ExtendedFunctionLogic.qml +FlagsComboBox 2.0 FlagsComboBox.qml FlickableGeometrySection 2.0 FlickableGeometrySection.qml FlickableSection 2.0 FlickableSection.qml FontComboBox 2.0 FontComboBox.qml diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/CustomComboBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/CustomComboBox.qml index 64507bf5241..8da528a867a 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/CustomComboBox.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/CustomComboBox.qml @@ -14,7 +14,6 @@ StudioControls.ComboBox { required property var itemsModel required property Item mainRoot - required property var rootView readonly property int popupHeight: Math.min(800, col.height + 2) @@ -27,23 +26,23 @@ StudioControls.ComboBox { } function calculateWindowGeometry() { - let globalPos = root.rootView.globalPos(mainRoot.mapFromItem(root, 0, 0)) - let screenRect = root.rootView.screenRect(); + let gPos = globalPos(mainRoot.mapFromItem(root, 0, 0)) + let scrRect = screenRect(); window.width = col.width + 2 // 2: scrollView left and right 1px margins - let newX = globalPos.x + root.width - window.width - if (newX < screenRect.x) - newX = globalPos.x + let newX = gPos.x + root.width - window.width + if (newX < scrRect.x) + newX = gPos.x - let newY = Math.min(screenRect.y + screenRect.height, - Math.max(screenRect.y, globalPos.y + root.height - 1)) + let newY = Math.min(scrRect.y + scrRect.height, + Math.max(scrRect.y, gPos.y + root.height - 1)) // Check if we have more space above or below the control, and put control on that side, // unless we have enough room for maximum size popup under the control let newHeight - let screenY = newY - screenRect.y - let availableHeight = screenRect.height - screenY + let screenY = newY - scrRect.y + let availableHeight = scrRect.height - screenY if (availableHeight > screenY || availableHeight > root.popupHeight) { newHeight = Math.min(root.popupHeight, availableHeight) } else { diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorcontextobject.cpp b/src/plugins/qmldesigner/components/materialeditor/materialeditorcontextobject.cpp index 5661436a8f3..664006fb7a5 100644 --- a/src/plugins/qmldesigner/components/materialeditor/materialeditorcontextobject.cpp +++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorcontextobject.cpp @@ -3,35 +3,30 @@ #include "materialeditorcontextobject.h" -#include #include #include #include -#include -#include -#include #include #include #include #include -#include #include #include #include #include #include +#include #include -#include - -#include +#include +#include namespace QmlDesigner { -MaterialEditorContextObject::MaterialEditorContextObject(QQmlContext *context, QObject *parent) +MaterialEditorContextObject::MaterialEditorContextObject(QQuickWidget *widget, QObject *parent) : QObject(parent) - , m_qmlContext(context) + , m_quickWidget(widget) { qmlRegisterUncreatableType("MaterialToolBarAction", 1, 0, "ToolBarAction", "Enum type"); } @@ -41,7 +36,7 @@ QQmlComponent *MaterialEditorContextObject::specificQmlComponent() if (m_specificQmlComponent) return m_specificQmlComponent; - m_specificQmlComponent = new QQmlComponent(m_qmlContext->engine(), this); + m_specificQmlComponent = new QQmlComponent(m_quickWidget->rootContext()->engine(), this); m_specificQmlComponent->setData(m_specificQmlData.toUtf8(), QUrl::fromLocalFile("specifics.qml")); return m_specificQmlComponent; @@ -543,4 +538,18 @@ void MaterialEditorContextObject::goIntoComponent() DocumentManager::goIntoComponent(m_selectedMaterial); } +QRect MaterialEditorContextObject::screenRect() const +{ + if (m_quickWidget && m_quickWidget->screen()) + return m_quickWidget->screen()->availableGeometry(); + return {}; +} + +QPoint MaterialEditorContextObject::globalPos(const QPoint &point) const +{ + if (m_quickWidget) + return m_quickWidget->mapToGlobal(point); + return point; +} + } // QmlDesigner diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorcontextobject.h b/src/plugins/qmldesigner/components/materialeditor/materialeditorcontextobject.h index 8772f3e1d2b..0ae8e1016f1 100644 --- a/src/plugins/qmldesigner/components/materialeditor/materialeditorcontextobject.h +++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorcontextobject.h @@ -6,13 +6,16 @@ #include #include -#include -#include -#include -#include #include +#include #include -#include +#include + +QT_BEGIN_NAMESPACE +class QQmlComponent; +class QQmlPropertyMap; +class QQuickWidget; +QT_END_NAMESPACE namespace QmlDesigner { @@ -44,7 +47,7 @@ class MaterialEditorContextObject : public QObject Q_PROPERTY(QQmlPropertyMap *backendValues READ backendValues WRITE setBackendValues NOTIFY backendValuesChanged) public: - MaterialEditorContextObject(QQmlContext *context, QObject *parent = nullptr); + MaterialEditorContextObject(QQuickWidget *widget, QObject *parent = nullptr); QUrl specificsUrl() const { return m_specificsUrl; } QString specificQmlData() const {return m_specificQmlData; } @@ -76,6 +79,9 @@ public: Q_INVOKABLE bool isBlocked(const QString &propName) const; Q_INVOKABLE void goIntoComponent(); + Q_INVOKABLE QRect screenRect() const; + Q_INVOKABLE QPoint globalPos(const QPoint &point) const; + enum ToolBarAction { ApplyToSelected = 0, ApplyToSelectedAdd, @@ -146,7 +152,7 @@ private: QUrl m_specificsUrl; QString m_specificQmlData; QQmlComponent *m_specificQmlComponent = nullptr; - QQmlContext *m_qmlContext = nullptr; + QQuickWidget *m_quickWidget = nullptr; QString m_stateName; QStringList m_allStateNames; diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.cpp b/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.cpp index f0ce34a0e62..661f1ad3788 100644 --- a/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.cpp +++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.cpp @@ -79,15 +79,15 @@ public: }; MaterialEditorQmlBackend::MaterialEditorQmlBackend(MaterialEditorView *materialEditor) - : m_view(new QQuickWidget) + : m_quickWidget(Utils::makeUniqueObjectPtr()) , m_materialEditorTransaction(new MaterialEditorTransaction(materialEditor)) - , m_contextObject(new MaterialEditorContextObject(m_view->rootContext())) + , m_contextObject(new MaterialEditorContextObject(m_quickWidget.get())) , m_materialEditorImageProvider(new MaterialEditorImageProvider()) { - m_view->setObjectName(Constants::OBJECT_NAME_MATERIAL_EDITOR); - m_view->setResizeMode(QQuickWidget::SizeRootObjectToView); - m_view->engine()->addImportPath(propertyEditorResourcesPath() + "/imports"); - m_view->engine()->addImageProvider("materialEditor", m_materialEditorImageProvider); + m_quickWidget->setObjectName(Constants::OBJECT_NAME_MATERIAL_EDITOR); + m_quickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView); + m_quickWidget->engine()->addImportPath(propertyEditorResourcesPath() + "/imports"); + m_quickWidget->engine()->addImageProvider("materialEditor", m_materialEditorImageProvider); m_contextObject->setBackendValues(&m_backendValuesPropertyMap); m_contextObject->setModel(materialEditor->model()); context()->setContextObject(m_contextObject.data()); @@ -188,7 +188,7 @@ void MaterialEditorQmlBackend::setValue(const QmlObjectNode &, const PropertyNam QQmlContext *MaterialEditorQmlBackend::context() const { - return m_view->rootContext(); + return m_quickWidget->rootContext(); } MaterialEditorContextObject *MaterialEditorQmlBackend::contextObject() const @@ -198,12 +198,12 @@ MaterialEditorContextObject *MaterialEditorQmlBackend::contextObject() const QQuickWidget *MaterialEditorQmlBackend::widget() const { - return m_view; + return m_quickWidget.get(); } void MaterialEditorQmlBackend::setSource(const QUrl &url) { - m_view->setSource(url); + m_quickWidget->setSource(url); } QmlAnchorBindingProxy &MaterialEditorQmlBackend::backendAnchorBinding() @@ -214,7 +214,7 @@ QmlAnchorBindingProxy &MaterialEditorQmlBackend::backendAnchorBinding() void MaterialEditorQmlBackend::updateMaterialPreview(const QPixmap &pixmap) { m_materialEditorImageProvider->setPixmap(pixmap); - QMetaObject::invokeMethod(m_view->rootObject(), "refreshPreview"); + QMetaObject::invokeMethod(m_quickWidget->rootObject(), "refreshPreview"); } DesignerPropertyMap &MaterialEditorQmlBackend::backendValuesPropertyMap() diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.h b/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.h index 65649329a23..6f9d6014e97 100644 --- a/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.h +++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.h @@ -7,6 +7,8 @@ #include "qmlanchorbindingproxy.h" #include "qmlmodelnodeproxy.h" +#include + #include class PropertyEditorValue; @@ -58,13 +60,13 @@ private: MaterialEditorView *materialEditor); PropertyName auxNamePostFix(const PropertyName &propertyName); - QQuickWidget *m_view = nullptr; + Utils::UniqueObjectPtr m_quickWidget = nullptr; QmlAnchorBindingProxy m_backendAnchorBinding; QmlModelNodeProxy m_backendModelNode; DesignerPropertyMap m_backendValuesPropertyMap; QScopedPointer m_materialEditorTransaction; QScopedPointer m_contextObject; - MaterialEditorImageProvider *m_materialEditorImageProvider = nullptr; + QPointer m_materialEditorImageProvider; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp index bf9066b0b6a..bc405057340 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp @@ -3449,10 +3449,10 @@ QVariant PropertyMetaInfo::castedValue(const QVariant &value) const if constexpr (!useProjectStorage()) { const QVariant variant = value; QVariant copyVariant = variant; - if (isEnumType() || variant.canConvert()) - return variant; - const TypeName &typeName = propertyTypeName(); + // skip casting flags and keep them as int. TODO: use flags as enums + if (isEnumType() || variant.canConvert() || typeName.endsWith("Flags")) + return variant; QVariant::Type typeId = nodeMetaInfoPrivateData()->variantTypeId(propertyName()); From e1c9720c09169c3d95fa4e95e9833dafab1ba110 Mon Sep 17 00:00:00 2001 From: Pranta Dastider Date: Mon, 4 Mar 2024 16:36:28 +0100 Subject: [PATCH 141/176] QmlDesigner: Create a document to list Qt runtime versions Qt Design Studio uses a specific Qt Runtime version to run the projects. This document lists the Qt runtime versions used for different Qt Design Studio releases. Fixes: QDS-12052 Change-Id: Ia9411393db4d800945c85ac67e4a9872a611308c Reviewed-by: Johanna Vanhatapio Reviewed-by: Thomas Hartmann --- .../qtquick/qtquick-modules-with-plugins.qdoc | 4 +- doc/qtcreator/src/vcs/creator-vcs-git.qdoc | 2 +- .../doc/images/studio-qt-development-kit.webp | Bin 2460 -> 2376 bytes .../src/qtdesignstudio-developer-topics.qdoc | 7 +++- ...signstudio-finding-qt-runtime-version.qdoc | 38 ++++++++++++++++++ .../qtdesignstudio-qt-runtime-version.qdocinc | 10 +++++ .../src/qtdesignstudio-toc.qdoc | 1 + 7 files changed, 57 insertions(+), 5 deletions(-) create mode 100644 doc/qtdesignstudio/src/qtdesignstudio-finding-qt-runtime-version.qdoc create mode 100644 doc/qtdesignstudio/src/qtdesignstudio-qt-runtime-version.qdocinc diff --git a/doc/qtcreator/src/qtquick/qtquick-modules-with-plugins.qdoc b/doc/qtcreator/src/qtquick/qtquick-modules-with-plugins.qdoc index 97cf62bc015..299bd057720 100644 --- a/doc/qtcreator/src/qtquick/qtquick-modules-with-plugins.qdoc +++ b/doc/qtcreator/src/qtquick/qtquick-modules-with-plugins.qdoc @@ -120,9 +120,7 @@ the emulation layer must be built with the same Qt version and compiler as the QML modules. - In \QDS, find the Qt version in the bottom toolbar next to - \inlineimage icons/settings.png. - \image studio-qt-development-kit.webp + \include qtdesignstudio-qt-runtime-version.qdocinc qt-runtime-version A plugin should behave differently depending on whether it is run by the emulation layer or an application. For example, animations should not be run diff --git a/doc/qtcreator/src/vcs/creator-vcs-git.qdoc b/doc/qtcreator/src/vcs/creator-vcs-git.qdoc index 0b0cbb0ee20..b9a785fa908 100644 --- a/doc/qtcreator/src/vcs/creator-vcs-git.qdoc +++ b/doc/qtcreator/src/vcs/creator-vcs-git.qdoc @@ -10,7 +10,7 @@ /*! \page creator-vcs-git.html \if defined(qtdesignstudio) - \previouspage studio-developer-topics.html + \previouspage studio-finding-the-qt-runtime-version.html \nextpage studio-porting-projects.html \title Using Git diff --git a/doc/qtdesignstudio/examples/doc/images/studio-qt-development-kit.webp b/doc/qtdesignstudio/examples/doc/images/studio-qt-development-kit.webp index ab791bfc0b676901601828e130e2e0688c989851..f6613be35ecff0c10376cc90bfa3aad666478cc0 100644 GIT binary patch delta 2035 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 O3z%VS2F1yJ9J>Gy>+aG3 diff --git a/doc/qtdesignstudio/src/qtdesignstudio-developer-topics.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-developer-topics.qdoc index 7a96a9a41e4..bc99a9c2c0d 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-developer-topics.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-developer-topics.qdoc @@ -4,7 +4,7 @@ /*! \previouspage studio-packaging.html \page studio-developer-topics.html - \nextpage creator-vcs-git.html + \nextpage studio-finding-the-qt-runtime-version.html \title Developer Topics @@ -17,6 +17,11 @@ \endtable \list + \li \l{Finding the Qt Runtime Version} + + \QDS runs projects using a specific version of Qt. Identify + which version of Qt your \QDS uses. + \li \l{Using Git} You can add the project files to the Git version control, so that diff --git a/doc/qtdesignstudio/src/qtdesignstudio-finding-qt-runtime-version.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-finding-qt-runtime-version.qdoc new file mode 100644 index 00000000000..cb9c7e86e99 --- /dev/null +++ b/doc/qtdesignstudio/src/qtdesignstudio-finding-qt-runtime-version.qdoc @@ -0,0 +1,38 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +/*! + \previouspage studio-developer-topics.html + \page studio-finding-the-qt-runtime-version.html + \nextpage creator-vcs-git.html + + \title Finding the Qt Runtime Version + + \include qtdesignstudio-qt-runtime-version.qdocinc qt-runtime-version + + Find Qt runtime versions for previous \QDS releases in the table below. + + \table + \header + \li \QDS + \li Qt Runtime Version + \row + \li 4.0 + \li 6.4.1 and 5.15.5 + \row + \li 4.1 + \li 6.5.1 + \row + \li 4.2 + \li 6.5.1 + \row + \li 4.3 + \li 6.5.2 + \row + \li 4.3.2 + \li 6.6.0 + \row + \li 4.4 + \li 6.6.2 + \endtable +*/ diff --git a/doc/qtdesignstudio/src/qtdesignstudio-qt-runtime-version.qdocinc b/doc/qtdesignstudio/src/qtdesignstudio-qt-runtime-version.qdocinc new file mode 100644 index 00000000000..fb74f9180db --- /dev/null +++ b/doc/qtdesignstudio/src/qtdesignstudio-qt-runtime-version.qdocinc @@ -0,0 +1,10 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +//! [qt-runtime-version] + \QDS runs projects using a specific version of Qt. + In \QDS, find the Qt runtime version in the bottom toolbar. + + \image studio-qt-development-kit.webp +//! [qt-runtime-version] +*/ diff --git a/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc index c2109082982..e1c7b4733e3 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc @@ -253,6 +253,7 @@ \endlist \li \l{Developer Topics} \list + \li \l{Finding the Qt Runtime Version} \li \l{Using Git} \li \l{Converting Qt 5 Projects into Qt 6 Projects} \li \l{Converting UI Projects to Applications} From 7f9f223aafbd77a3b33fb724d393a9f863370635 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Thu, 7 Mar 2024 17:19:21 +0200 Subject: [PATCH 142/176] QmlDesigner: Match CustomComboBox's popup width with the ComboBox Change-Id: Idc12775e2a886e469d73e3e1f0e678ec3ddbc62c Reviewed-by: Miikka Heikkinen --- .../imports/StudioControls/CustomComboBox.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/CustomComboBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/CustomComboBox.qml index 8da528a867a..df3c86aa7c1 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/CustomComboBox.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/CustomComboBox.qml @@ -29,7 +29,7 @@ StudioControls.ComboBox { let gPos = globalPos(mainRoot.mapFromItem(root, 0, 0)) let scrRect = screenRect(); - window.width = col.width + 2 // 2: scrollView left and right 1px margins + window.width = Math.max(root.width - root.actionIndicator.width, col.width + 2) // 2: scrollView left and right 1px margins let newX = gPos.x + root.width - window.width if (newX < scrRect.x) From 5d497a3b7cbe598418a3c8fa24d1fcd727158bd0 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Thu, 7 Mar 2024 22:01:29 +0200 Subject: [PATCH 143/176] QmlDesigner: Add screenRect() and globalPos() to prop. editor context ...so that CustomComboBox's popup work. Also small tweaks. Change-Id: I8c4638e6710db80aa59da326af868ab6f6917e55 Reviewed-by: Reviewed-by: Miikka Heikkinen Reviewed-by: Qt CI Patch Build Bot --- .../propertyeditorcontextobject.cpp | 37 +++++++++++++------ .../propertyeditorcontextobject.h | 23 +++++++----- .../propertyeditorqmlbackend.cpp | 2 +- 3 files changed, 40 insertions(+), 22 deletions(-) diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.cpp index 03199d274d1..591ce5a57fe 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.cpp @@ -2,17 +2,16 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "propertyeditorcontextobject.h" -#include "timelineeditor/easingcurvedialog.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include "abstractview.h" +#include "easingcurvedialog.h" +#include "nodemetainfo.h" +#include "qmldesignerconstants.h" +#include "qml3dnode.h" +#include "qmldesignerplugin.h" +#include "qmlmodelnodeproxy.h" +#include "qmlobjectnode.h" +#include "qmltimeline.h" #include #include @@ -73,13 +72,15 @@ namespace QmlDesigner { static Q_LOGGING_CATEGORY(urlSpecifics, "qtc.propertyeditor.specifics", QtWarningMsg) - PropertyEditorContextObject::PropertyEditorContextObject(QObject *parent) + PropertyEditorContextObject::PropertyEditorContextObject(Quick2PropertyEditorView *widget, + QObject *parent) : QObject(parent) , m_isBaseState(false) , m_selectionChanged(false) , m_backendValues(nullptr) , m_qmlComponent(nullptr) , m_qmlContext(nullptr) + , m_quickWidget(widget) {} QString PropertyEditorContextObject::convertColorToString(const QVariant &color) @@ -620,6 +621,20 @@ void PropertyEditorContextObject::verifyInsightImport() m_model->changeImports({import}, {}); } +QRect PropertyEditorContextObject::screenRect() const +{ + if (m_quickWidget && m_quickWidget->screen()) + return m_quickWidget->screen()->availableGeometry(); + return {}; +} + +QPoint PropertyEditorContextObject::globalPos(const QPoint &point) const +{ + if (m_quickWidget) + return m_quickWidget->mapToGlobal(point); + return point; +} + void EasingCurveEditor::registerDeclarativeType() { qmlRegisterType("HelperWidgets", 2, 0, "EasingCurveEditor"); diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.h b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.h index 2f5ad4b4ebd..ba5896d798f 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.h +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.h @@ -3,8 +3,9 @@ #pragma once -#include -#include +#include "model.h" +#include "modelnode.h" +#include "quick2propertyeditorview.h" #include #include @@ -14,8 +15,6 @@ #include #include -#include - namespace QmlDesigner { class PropertyEditorContextObject : public QObject @@ -42,9 +41,9 @@ class PropertyEditorContextObject : public QObject Q_PROPERTY(bool hasActiveTimeline READ hasActiveTimeline NOTIFY hasActiveTimelineChanged) - Q_PROPERTY(QQmlPropertyMap* backendValues READ backendValues WRITE setBackendValues NOTIFY backendValuesChanged) + Q_PROPERTY(QQmlPropertyMap *backendValues READ backendValues WRITE setBackendValues NOTIFY backendValuesChanged) - Q_PROPERTY(QQmlComponent* specificQmlComponent READ specificQmlComponent NOTIFY specificQmlComponentChanged) + Q_PROPERTY(QQmlComponent *specificQmlComponent READ specificQmlComponent NOTIFY specificQmlComponentChanged) Q_PROPERTY(bool hasMultiSelection READ hasMultiSelection WRITE setHasMultiSelection NOTIFY hasMultiSelectionChanged) @@ -53,7 +52,7 @@ class PropertyEditorContextObject : public QObject Q_PROPERTY(QStringList insightCategories MEMBER m_insightCategories NOTIFY insightCategoriesChanged) public: - PropertyEditorContextObject(QObject *parent = nullptr); + PropertyEditorContextObject(Quick2PropertyEditorView *widget, QObject *parent = nullptr); QUrl specificsUrl() const {return m_specificsUrl; } QString specificQmlData() const {return m_specificQmlData; } @@ -63,7 +62,7 @@ public: bool isBaseState() const { return m_isBaseState; } bool selectionChanged() const { return m_selectionChanged; } - QQmlPropertyMap* backendValues() const { return m_backendValues; } + QQmlPropertyMap *backendValues() const { return m_backendValues; } Q_INVOKABLE QString convertColorToString(const QVariant &color); Q_INVOKABLE QColor colorFromString(const QString &colorString); @@ -92,6 +91,9 @@ public: Q_INVOKABLE void verifyInsightImport(); + Q_INVOKABLE QRect screenRect() const; + Q_INVOKABLE QPoint globalPos(const QPoint &point) const; + QString activeDragSuffix() const; void setActiveDragSuffix(const QString &suffix); @@ -154,7 +156,7 @@ public slots: void setSelectionChanged(bool newSelectionChanged); - void setBackendValues(QQmlPropertyMap* newBackendValues); + void setBackendValues(QQmlPropertyMap *newBackendValues); void setModel(Model *model); @@ -171,7 +173,7 @@ private: bool m_isBaseState; bool m_selectionChanged; - QQmlPropertyMap* m_backendValues; + QQmlPropertyMap *m_backendValues; int m_majorVersion = 1; int m_minorVersion = 1; @@ -179,6 +181,7 @@ private: int m_minorQtQuickVersion = -1; QQmlComponent *m_qmlComponent; QQmlContext *m_qmlContext; + Quick2PropertyEditorView *m_quickWidget = nullptr; QPoint m_lastPos; diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp index 4e40d9b12cb..757777d656c 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp @@ -84,7 +84,7 @@ PropertyEditorQmlBackend::PropertyEditorQmlBackend(PropertyEditorView *propertyE : m_view(new Quick2PropertyEditorView(imageCache)) , m_propertyEditorTransaction(new PropertyEditorTransaction(propertyEditor)) , m_dummyPropertyEditorValue(new PropertyEditorValue()) - , m_contextObject(new PropertyEditorContextObject()) + , m_contextObject(new PropertyEditorContextObject(m_view)) { m_view->engine()->setOutputWarningsToStandardError(QmlDesignerPlugin::instance() ->settings().value(DesignerSettingsKey::SHOW_PROPERTYEDITOR_WARNINGS).toBool()); From d786001fa12c2d8eb667486b5c57117d7cdb3657 Mon Sep 17 00:00:00 2001 From: Shrief Gabr Date: Thu, 7 Mar 2024 10:50:35 +0200 Subject: [PATCH 144/176] EffectComposer: Add color channels to the Opacity Mask effect Task-number: QDS-11417 Change-Id: Ie35e4da6c12a78fd808a6264f643ce2b3b1f62d2 Reviewed-by: Mahmoud Badri Reviewed-by: Miikka Heikkinen --- .../EffectCompositionNodeUniform.qml | 5 ++- .../effectComposerQmlSources/ValueChannel.qml | 15 +++++++ .../effectcomposer/effectcomposermodel.cpp | 10 ++++- src/plugins/effectcomposer/uniform.cpp | 43 ++++++++++++++----- src/plugins/effectcomposer/uniform.h | 1 + 5 files changed, 61 insertions(+), 13 deletions(-) create mode 100644 share/qtcreator/qmldesigner/effectComposerQmlSources/ValueChannel.qml diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectCompositionNodeUniform.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectCompositionNodeUniform.qml index c353f8260aa..71cbf94f8bc 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectCompositionNodeUniform.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectCompositionNodeUniform.qml @@ -18,7 +18,10 @@ Item { Component.onCompleted: { if (uniformType === "int") { - valueLoader.source = "ValueInt.qml" + if (uniformControlType === "channel") + valueLoader.source = "ValueChannel.qml" + else + valueLoader.source = "ValueInt.qml" } else if (uniformType === "vec2") { valueLoader.source = "ValueVec2.qml" } else if (uniformType === "vec3") { diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueChannel.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueChannel.qml new file mode 100644 index 00000000000..a089caf3efb --- /dev/null +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueChannel.qml @@ -0,0 +1,15 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQuick +import StudioTheme as StudioTheme +import StudioControls as StudioControls + +Row { + StudioControls.ComboBox { + model: ["R", "G", "B", "A"] + actionIndicatorVisible: false + Component.onCompleted: currentIndex = uniformValue ?? 3 + onActivated: uniformValue = currentIndex + } +} diff --git a/src/plugins/effectcomposer/effectcomposermodel.cpp b/src/plugins/effectcomposer/effectcomposermodel.cpp index 5c2b1fc3cd6..5f782c7cbe7 100644 --- a/src/plugins/effectcomposer/effectcomposermodel.cpp +++ b/src/plugins/effectcomposer/effectcomposermodel.cpp @@ -424,6 +424,7 @@ QString variantAsDataString(const Uniform::Type type, const Uniform::Type contro s = variant.toBool() ? QString("true") : QString("false"); break; case Uniform::Type::Int: + case Uniform::Type::Channel: s = QString::number(variant.toInt()); break; case Uniform::Type::Float: @@ -497,8 +498,10 @@ QJsonObject nodeToJson(const CompositionNode &node) QJsonObject uniformObject; uniformObject.insert("name", QString(uniform->name())); QString type = Uniform::stringFromType(uniform->type()); + uniformObject.insert("type", type); - if (uniform->type() == Uniform::Type::Define) { + + if (uniform->type() == Uniform::Type::Define || uniform->type() == Uniform::Type::Channel) { QString controlType = Uniform::stringFromType(uniform->controlType()); if (controlType != type) uniformObject.insert("controlType", controlType); @@ -1140,6 +1143,8 @@ QString EffectComposerModel::valueAsString(const Uniform &uniform) return getImageElementName(uniform, true); } else if (uniform.type() == Uniform::Type::Color) { return QString("\"%1\"").arg(uniform.value().toString()); + } else if (uniform.type() == Uniform::Type::Channel) { + return QString::number(uniform.value().toInt()); } else if (uniform.type() == Uniform::Type::Define) { if (uniform.controlType() == Uniform::Type::Int) return QString::number(uniform.value().toInt()); @@ -1159,6 +1164,7 @@ QString EffectComposerModel::valueAsBinding(const Uniform &uniform) || uniform.type() == Uniform::Type::Int || uniform.type() == Uniform::Type::Float || uniform.type() == Uniform::Type::Color + || uniform.type() == Uniform::Type::Channel || uniform.type() == Uniform::Type::Define) { return "g_propertyData." + uniform.name(); } else if (uniform.type() == Uniform::Type::Vec2) { @@ -1205,6 +1211,8 @@ QString EffectComposerModel::valueAsVariable(const Uniform &uniform) } else if (uniform.type() == Uniform::Type::Color) { QColor c = uniform.value().value(); return QString("vec4(%1, %2, %3, %4)").arg(c.redF(), c.greenF(), c.blueF(), c.alphaF()); + } else if (uniform.type() == Uniform::Type::Channel) { + return QString::number(uniform.value().toInt()); } else { qWarning() << QString("Unhandled const variable type: %1").arg(int(uniform.type())).toLatin1(); return QString(); diff --git a/src/plugins/effectcomposer/uniform.cpp b/src/plugins/effectcomposer/uniform.cpp index 2fe6bfd2f5f..98d5ffd336f 100644 --- a/src/plugins/effectcomposer/uniform.cpp +++ b/src/plugins/effectcomposer/uniform.cpp @@ -22,7 +22,6 @@ Uniform::Uniform(const QString &effectName, const QJsonObject &propObj, const QS m_name = propObj.value("name").toString(); m_description = propObj.value("description").toString(); m_type = Uniform::typeFromString(propObj.value("type").toString()); - m_controlType = m_type; defaultValue = propObj.value("defaultValue").toString(); m_displayName = propObj.value("displayName").toString(); @@ -45,11 +44,8 @@ Uniform::Uniform(const QString &effectName, const QJsonObject &propObj, const QS g_propertyData[mipmapProperty] = m_enableMipmap; } - if (m_type == Type::Define) { - QString controlType = propObj.value("controlType").toString(); - if (!controlType.isEmpty()) - m_controlType = Uniform::typeFromString(controlType); - } + QString controlType = propObj.value("controlType").toString(); + m_controlType = controlType.isEmpty() ? m_type : Uniform::typeFromString(controlType); m_customValue = propObj.value("customValue").toString(); m_useCustomValue = getBoolValue(propObj.value("useCustomValue"), false); @@ -245,8 +241,9 @@ R"( break; } case Type::Int: { - QString typeSpec = -R"( + if (m_controlType == Uniform::Type::Int) { + QString typeSpec = + R"( SpinBox { minimumValue: %1 maximumValue: %2 @@ -257,10 +254,25 @@ R"( implicitWidth: StudioTheme.Values.singleControlColumnWidth + StudioTheme.Values.actionIndicatorWidth } -)"; - specs += typeSpec.arg(m_minValue.toString(), m_maxValue.toString(), m_name); - break; + )"; + specs += typeSpec.arg(m_minValue.toString(), m_maxValue.toString(), m_name); + } else if (m_controlType == Uniform::Type::Channel) { + QString typeSpec = + R"( + ComboBox { + model: ["R", "G", "B", "A"] + backendValue: backendValues.%1 + manualMapping: true + + onCompressedActivated: backendValue.value = currentIndex + onValueFromBackendChanged: currentIndex = backendValue.value + Component.onCompleted: currentIndex = backendValue.value + } + )"; + specs += typeSpec.arg(m_name); + } } + break; case Type::Float: { QString typeSpec = R"( @@ -410,6 +422,8 @@ QVariant Uniform::getInitializedVariant(bool maxValue) return maxValue ? QVector4D(1.0, 1.0, 1.0, 1.0) : QVector4D(0.0, 0.0, 0.0, 0.0); case Uniform::Type::Color: return maxValue ? QColor::fromRgbF(1.0f, 1.0f, 1.0f, 1.0f) : QColor::fromRgbF(0.0f, 0.0f, 0.0f, 0.0f); + case Uniform::Type::Channel: + return 3; // sets default channel to alpha case Uniform::Type::Define: if (m_controlType == Uniform::Type::Bool) return maxValue ? true : false; @@ -460,6 +474,7 @@ QVariant Uniform::valueStringToVariant(const QString &value) } break; case Type::Sampler: + case Type::Channel: variant = value; break; case Uniform::Type::Define: @@ -491,6 +506,8 @@ QString Uniform::stringFromType(Uniform::Type type, bool isShader) return isShader ? QString("vec4") : QString("color"); else if (type == Type::Sampler) return "sampler2D"; + else if (type == Type::Channel) + return "channel"; else if (type == Type::Define) return "define"; @@ -516,6 +533,8 @@ Uniform::Type Uniform::typeFromString(const QString &typeString) return Uniform::Type::Color; else if (typeString == "sampler2D" || typeString == "image") //TODO: change image to sample2D in all QENs return Uniform::Type::Sampler; + else if (typeString == "channel") + return Uniform::Type::Channel; else if (typeString == "define") return Uniform::Type::Define; @@ -539,6 +558,8 @@ QString Uniform::typeToProperty(Uniform::Type type) return "vector4d"; else if (type == Uniform::Type::Color) return "color"; + else if (type == Uniform::Type::Channel) + return "channel"; else if (type == Uniform::Type::Sampler || type == Uniform::Type::Define) return "var"; diff --git a/src/plugins/effectcomposer/uniform.h b/src/plugins/effectcomposer/uniform.h index b478b2bed89..d3a39006510 100644 --- a/src/plugins/effectcomposer/uniform.h +++ b/src/plugins/effectcomposer/uniform.h @@ -41,6 +41,7 @@ public: Vec4, Color, Sampler, + Channel, Define }; From 836c0a11e48712370f13b7a9dc74c9b983750e9f Mon Sep 17 00:00:00 2001 From: Shrief Gabr Date: Thu, 7 Mar 2024 11:43:13 +0200 Subject: [PATCH 145/176] EffectComposer: Open effectComposerNodes/images by default in UrlChooser Task-number: QDS-11464 Change-Id: I96bb7e359bd41637f0b1906a523c8a5de190981d Reviewed-by: Miikka Heikkinen Reviewed-by: Mahmoud Badri --- .../qmldesigner/effectComposerQmlSources/ValueImage.qml | 1 + .../imports/HelperWidgets/UrlChooser.qml | 5 ++++- src/plugins/effectcomposer/effectcomposerwidget.cpp | 5 +++++ src/plugins/effectcomposer/effectcomposerwidget.h | 1 + .../components/propertyeditor/fileresourcesmodel.cpp | 4 ++-- .../components/propertyeditor/fileresourcesmodel.h | 2 +- 6 files changed, 14 insertions(+), 4 deletions(-) diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueImage.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueImage.qml index 3bdab3a0a0e..bed9dfdce0c 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueImage.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueImage.qml @@ -13,6 +13,7 @@ Row { HelperWidgets.UrlChooser { backendValue: uniformBackendValue + resourcesPath: EffectComposerBackend.rootView.imagesPath() actionIndicatorVisible: false comboBox.width: Math.min(parent.width - 70, 300) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/UrlChooser.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/UrlChooser.qml index 5d65bb09fc4..c73e736ef42 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/UrlChooser.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/UrlChooser.qml @@ -28,6 +28,9 @@ Row { // Current item property string absoluteFilePath: "" + // If this property is set, this path will be opened instead of the default path + property string resourcesPath + property alias comboBox: comboBox property alias spacer: spacer property alias actionIndicatorVisible: comboBox.actionIndicatorVisible @@ -525,7 +528,7 @@ Row { icon: StudioTheme.Constants.addFile iconColor: root.textColor onClicked: { - fileModel.openFileDialog() + fileModel.openFileDialog(resourcesPath) if (fileModel.fileName !== "") { root.backendValue.value = fileModel.fileName root.absoluteFilePath = fileModel.resolve(root.backendValue.value) diff --git a/src/plugins/effectcomposer/effectcomposerwidget.cpp b/src/plugins/effectcomposer/effectcomposerwidget.cpp index 3ae6e7b62f8..dd1d5e926d2 100644 --- a/src/plugins/effectcomposer/effectcomposerwidget.cpp +++ b/src/plugins/effectcomposer/effectcomposerwidget.cpp @@ -197,6 +197,11 @@ QString EffectComposerWidget::uniformDefaultImage(const QString &nodeName, const return m_effectComposerNodesModel->defaultImagesForNode(nodeName).value(uniformName); } +QString EffectComposerWidget::imagesPath() const +{ + return Core::ICore::resourcePath("qmldesigner/effectComposerNodes/images").toString(); +} + QSize EffectComposerWidget::sizeHint() const { return {420, 420}; diff --git a/src/plugins/effectcomposer/effectcomposerwidget.h b/src/plugins/effectcomposer/effectcomposerwidget.h index fb4c818f4f2..aa9cb750d8e 100644 --- a/src/plugins/effectcomposer/effectcomposerwidget.h +++ b/src/plugins/effectcomposer/effectcomposerwidget.h @@ -52,6 +52,7 @@ public: Q_INVOKABLE QPoint globalPos(const QPoint &point) const; Q_INVOKABLE QString uniformDefaultImage(const QString &nodeName, const QString &uniformName) const; + Q_INVOKABLE QString imagesPath() const; QSize sizeHint() const override; diff --git a/src/plugins/qmldesigner/components/propertyeditor/fileresourcesmodel.cpp b/src/plugins/qmldesigner/components/propertyeditor/fileresourcesmodel.cpp index 3c419a3e82d..2942ca86c76 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/fileresourcesmodel.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/fileresourcesmodel.cpp @@ -114,9 +114,9 @@ QList FileResourcesModel::model() const return m_model; } -void FileResourcesModel::openFileDialog() +void FileResourcesModel::openFileDialog(const QString &customPath) { - QString resourcePath = m_path.toLocalFile(); + QString resourcePath = customPath.isEmpty() ? m_path.toLocalFile() : customPath; bool resourcePathChanged = m_lastResourcePath != resourcePath; m_lastResourcePath = resourcePath; diff --git a/src/plugins/qmldesigner/components/propertyeditor/fileresourcesmodel.h b/src/plugins/qmldesigner/components/propertyeditor/fileresourcesmodel.h index 4bca3f531ac..d382424c8ac 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/fileresourcesmodel.h +++ b/src/plugins/qmldesigner/components/propertyeditor/fileresourcesmodel.h @@ -69,7 +69,7 @@ public: void refreshModel(); - Q_INVOKABLE void openFileDialog(); + Q_INVOKABLE void openFileDialog(const QString &customPath = {}); Q_INVOKABLE QString resolve(const QString &relative) const; Q_INVOKABLE bool isLocal(const QString &path) const; From 21299fe1f15c8384b6bb47a872a1fe4488e6cae2 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Fri, 8 Mar 2024 17:11:22 +0200 Subject: [PATCH 146/176] QmlDesigner: Generalize CustomComboBox Make CustomComboBox only expose a component for its content so that any content can be added. Not necessarily an item list. Change-Id: Idc351351f35e5ca558a28422dd475cd5c800c27a Reviewed-by: Miikka Heikkinen --- .../imports/HelperWidgets/FlagsComboBox.qml | 47 ++++++++++++++----- .../imports/StudioControls/CustomComboBox.qml | 28 ++++------- 2 files changed, 42 insertions(+), 33 deletions(-) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FlagsComboBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FlagsComboBox.qml index d6d52d8d16c..bb08fdcdff9 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FlagsComboBox.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FlagsComboBox.qml @@ -26,11 +26,15 @@ itemsModel: ListModel { StudioControls.CustomComboBox { id: root + required property var itemsModel + property variant backendValue property bool showExtendedFunctionButton: true Connections { + id: backendValueConnection + target: backendValue function onValueChangedQml() { @@ -39,7 +43,7 @@ StudioControls.CustomComboBox { for (let i = 1; i < root.itemsModel.count; ++i) { let flag = (root.backendValue.value >> (i - 1)) & 1; - root.itemAt(i).checked = flag + root.popupItem.itemAt(i).checked = flag if (flag) { selectedItem = root.itemsModel.get(i).name @@ -54,21 +58,38 @@ StudioControls.CustomComboBox { } } + Component.onCompleted: backendValueConnection.onValueChangedQml() + model: [qsTr("empty")] - itemDelegate: StudioControls.CheckBox { - text: name - actionIndicatorVisible: false + popupComponent: Column { + padding: 5 + spacing: 2 - onToggled: { - if (index === 0) { // Clear - root.itemAt(0).checked = false - root.backendValue.value = 0 - } else { - if (root.itemAt(index).checked) - root.backendValue.value |= flag - else - root.backendValue.value &= ~flag + function itemAt(idx) { + return repeater.itemAt(idx) + } + + Repeater { + id: repeater + + model: root.itemsModel + delegate: StudioControls.CheckBox { + text: name + actionIndicatorVisible: false + checked: (root.backendValue.value >> (index - 1)) & 1 + + onToggled: { + if (index === 0) { // Clear + root.popupItem.itemAt(0).checked = false + root.backendValue.value = 0 + } else { + if (root.popupItem.itemAt(index).checked) + root.backendValue.value |= flag + else + root.backendValue.value &= ~flag + } + } } } } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/CustomComboBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/CustomComboBox.qml index df3c86aa7c1..ba39201bdc8 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/CustomComboBox.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/CustomComboBox.qml @@ -10,26 +10,23 @@ import StudioTheme as StudioTheme StudioControls.ComboBox { id: root - required property Component itemDelegate - required property var itemsModel + required property Component popupComponent required property Item mainRoot - readonly property int popupHeight: Math.min(800, col.height + 2) + readonly property int popupHeight: Math.min(800, popupLoader.height + 2) + + property alias popupItem: popupLoader.item // hide default popup popup.width: 0 popup.height: 0 - function itemAt(idx) { - return repeater.itemAt(idx) - } - function calculateWindowGeometry() { let gPos = globalPos(mainRoot.mapFromItem(root, 0, 0)) let scrRect = screenRect(); - window.width = Math.max(root.width - root.actionIndicator.width, col.width + 2) // 2: scrollView left and right 1px margins + window.width = Math.max(root.width - root.actionIndicator.width, popupLoader.width + 2) // 2: scrollView left and right 1px margins let newX = gPos.x + root.width - window.width if (newX < scrRect.x) @@ -94,18 +91,9 @@ StudioControls.ComboBox { anchors.fill: parent anchors.margins: 1 - Column { - id: col - - padding: 5 - spacing: 2 - - Repeater { - id: repeater - - model: root.itemsModel - delegate: root.itemDelegate - } + Loader { + id: popupLoader + sourceComponent: root.popupComponent } } From 572205691fe0e86b6ad265444342939446adc36f Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 8 Mar 2024 17:18:47 +0200 Subject: [PATCH 147/176] Doubleclick on View3D item in 2D view will open 3D view Also if the click was over a visible model in View3D, that model will be selected. 3D view edit camera will be aligned to the scene camera currently in use. Fixes: QDS-12188 Change-Id: I25823dbbcf9d1706b2cdefb8c34aa78ecd93e407 Reviewed-by: Mahmoud Badri --- .../interfaces/nodeinstanceglobal.h | 1 + src/plugins/qmldesigner/CMakeLists.txt | 1 + .../components/edit3d/edit3dview.cpp | 14 ++- .../components/edit3d/edit3dview.h | 1 + .../components/formeditor/view3dtool.cpp | 114 ++++++++++++++++++ .../components/formeditor/view3dtool.h | 52 ++++++++ src/plugins/qmldesigner/qmldesignerplugin.cpp | 2 + .../qt5informationnodeinstanceserver.cpp | 57 ++++++++- .../qt5informationnodeinstanceserver.h | 3 +- 9 files changed, 238 insertions(+), 7 deletions(-) create mode 100644 src/plugins/qmldesigner/components/formeditor/view3dtool.cpp create mode 100644 src/plugins/qmldesigner/components/formeditor/view3dtool.h diff --git a/src/libs/qmlpuppetcommunication/interfaces/nodeinstanceglobal.h b/src/libs/qmlpuppetcommunication/interfaces/nodeinstanceglobal.h index 56a2796de09..a3012c99f53 100644 --- a/src/libs/qmlpuppetcommunication/interfaces/nodeinstanceglobal.h +++ b/src/libs/qmlpuppetcommunication/interfaces/nodeinstanceglobal.h @@ -46,6 +46,7 @@ enum class View3DActionType { ParticlesSeek, SyncEnvBackground, GetNodeAtPos, + GetNodeAtMainScenePos, SetBakeLightsView3D, SplitViewToggle, MaterialOverride, diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 09d0aa01c09..8412970d3b6 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -706,6 +706,7 @@ extend_qtc_plugin(QmlDesigner snappinglinecreator.cpp snappinglinecreator.h toolbox.cpp toolbox.h transitiontool.cpp transitiontool.h + view3dtool.cpp view3dtool.h ) extend_qtc_plugin(QmlDesigner diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp index 99bf125c982..763a8a38f1e 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp @@ -434,8 +434,16 @@ void Edit3DView::customNotification([[maybe_unused]] const AbstractView *view, [[maybe_unused]] const QList &nodeList, [[maybe_unused]] const QList &data) { - if (identifier == "asset_import_update") + if (identifier == "asset_import_update") { resetPuppet(); + } else if (identifier == "pick_3d_node_from_2d_scene" && data.size() == 1 && nodeList.size() == 1) { + // Pick via 2D view, data has pick coordinates in main scene coordinates + QTimer::singleShot(0, this, [=]() { + emitView3DAction(View3DActionType::GetNodeAtMainScenePos, + QVariantList{data[0], nodeList[0].internalId()}); + m_nodeAtPosReqType = NodeAtPosReqType::MainScenePick; + }); + } } /** @@ -483,6 +491,10 @@ void Edit3DView::nodeAtPosReady(const ModelNode &modelNode, const QVector3D &pos bool isModel = modelNode.metaInfo().isQtQuick3DModel(); if (!m_droppedFile.isEmpty() && isModel) emitCustomNotification("apply_asset_to_model3D", {modelNode}, {m_droppedFile}); // To MaterialBrowserView + } else if (m_nodeAtPosReqType == NodeAtPosReqType::MainScenePick) { + if (modelNode.isValid()) + setSelectedModelNode(modelNode); + emitView3DAction(View3DActionType::AlignViewToCamera, true); } m_droppedModelNode = {}; diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.h b/src/plugins/qmldesigner/components/edit3d/edit3dview.h index 88cac111a7a..63b815f392b 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.h +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.h @@ -105,6 +105,7 @@ private: TextureDrop, ContextMenu, AssetDrop, + MainScenePick, None }; diff --git a/src/plugins/qmldesigner/components/formeditor/view3dtool.cpp b/src/plugins/qmldesigner/components/formeditor/view3dtool.cpp new file mode 100644 index 00000000000..a494d467752 --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/view3dtool.cpp @@ -0,0 +1,114 @@ +// 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 "view3dtool.h" + +#include "designmodewidget.h" +#include "formeditorview.h" +#include "qmldesignerplugin.h" + +#include + +namespace QmlDesigner { + +View3DTool::View3DTool() + : QObject(), AbstractCustomTool() +{ +} + +View3DTool::~View3DTool() +{ +} + +void View3DTool::clear() +{ + m_view3dNode = {}; + AbstractFormEditorTool::clear(); +} + +void View3DTool::mouseMoveEvent(const QList &, QGraphicsSceneMouseEvent *) +{ +} + +void View3DTool::hoverMoveEvent(const QList &, QGraphicsSceneMouseEvent *) +{ +} + +void View3DTool::keyPressEvent(QKeyEvent *) +{ +} + +void View3DTool::keyReleaseEvent(QKeyEvent *) +{ +} + +void View3DTool::dragLeaveEvent(const QList &, QGraphicsSceneDragDropEvent *) +{ +} + +void View3DTool::dragMoveEvent(const QList &, QGraphicsSceneDragDropEvent *) +{ +} + +void View3DTool::mouseReleaseEvent(const QList &, + QGraphicsSceneMouseEvent *event) +{ + if (m_view3dNode.isValid()) { + QmlDesignerPlugin::instance()->mainWidget()->showDockWidget("Editor3D", true); + view()->emitCustomNotification("pick_3d_node_from_2d_scene", + {m_view3dNode}, {event->scenePos()}); + } + + view()->changeToSelectionTool(); +} + +void View3DTool::itemsAboutToRemoved(const QList &) +{ +} + +void View3DTool::instancesCompleted(const QList &) +{ +} + +void View3DTool::instancesParentChanged(const QList &) +{ +} + +void View3DTool::instancePropertyChange(const QList> &) +{ +} + +void View3DTool::formEditorItemsChanged(const QList &) +{ +} + +int View3DTool::wantHandleItem(const ModelNode &modelNode) const +{ + if (modelNode.metaInfo().isQtQuick3DView3D()) + return 30; + + return 0; +} + + +QString View3DTool::name() const +{ + return tr("View3D Tool"); +} + +void View3DTool::selectedItemsChanged(const QList &itemList) +{ + if (itemList.size() == 1) { + if (itemList[0]) { + ModelNode node = itemList[0]->qmlItemNode().modelNode(); + if (node.metaInfo().isQtQuick3DView3D()) { + m_view3dNode = node; + return; + } + } + } + + view()->changeToSelectionTool(); +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/formeditor/view3dtool.h b/src/plugins/qmldesigner/components/formeditor/view3dtool.h new file mode 100644 index 00000000000..eeadd3483bf --- /dev/null +++ b/src/plugins/qmldesigner/components/formeditor/view3dtool.h @@ -0,0 +1,52 @@ +// 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 "abstractcustomtool.h" + +#include "modelnode.h" + +namespace QmlDesigner { + +class View3DTool : public QObject, public AbstractCustomTool +{ + Q_OBJECT +public: + View3DTool(); + ~View3DTool(); + + void mouseMoveEvent(const QList &itemList, + QGraphicsSceneMouseEvent *event) override; + void hoverMoveEvent(const QList &itemList, + QGraphicsSceneMouseEvent *event) override; + void mouseReleaseEvent(const QList &itemList, + QGraphicsSceneMouseEvent *event) override; + void keyPressEvent(QKeyEvent *event) override; + void keyReleaseEvent(QKeyEvent *keyEvent) override; + + void dragLeaveEvent(const QList &itemList, + QGraphicsSceneDragDropEvent *event) override; + void dragMoveEvent(const QList &itemList, + QGraphicsSceneDragDropEvent *event) override; + + void itemsAboutToRemoved(const QList &itemList) override; + + void instancesCompleted(const QList &itemList) override; + void instancesParentChanged(const QList &itemList) override; + void instancePropertyChange(const QList> &propertyList) override; + + void clear() override; + + void formEditorItemsChanged(const QList &itemList) override; + + int wantHandleItem(const ModelNode &modelNode) const override; + + QString name() const override; + + void selectedItemsChanged(const QList &itemList) override; + +private: + ModelNode m_view3dNode; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/qmldesignerplugin.cpp b/src/plugins/qmldesigner/qmldesignerplugin.cpp index cd0ffaba263..601cf966813 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.cpp +++ b/src/plugins/qmldesigner/qmldesignerplugin.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #ifndef QDS_USE_PROJECTSTORAGE @@ -656,6 +657,7 @@ void QmlDesignerPlugin::enforceDelayedInitialize() d->viewManager.registerFormEditorTool(std::make_unique()); d->viewManager.registerFormEditorTool(std::make_unique(d->externalDependencies)); d->viewManager.registerFormEditorTool(std::make_unique()); + d->viewManager.registerFormEditorTool(std::make_unique()); if (Core::ICore::isQtDesignStudio()) { d->mainWidget.initialize(); diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp index 26c5fb9c702..1ce4e0364c8 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp @@ -474,6 +474,42 @@ void Qt5InformationNodeInstanceServer::getNodeAtPos([[maybe_unused]] const QPoin #endif } +void Qt5InformationNodeInstanceServer::getNodeAtMainScenePos( + [[maybe_unused]] const QPointF &pos, [[maybe_unused]] qint32 viewId) +{ +#ifdef QUICK3D_MODULE + // Pick a Quick3DModel at scene position in the main scene + auto helper = qobject_cast(m_3dHelper); + + if (!helper || !hasInstanceForId(viewId)) + return; + + ServerNodeInstance view = instanceForId(viewId); + auto viewObj = qobject_cast(view.internalObject()); + + if (viewObj) { + // Render the main view to make sure everything is up to date + updateNodesRecursive(viewObj); + renderWindow(); + + QPointF viewPos = viewObj->mapFromScene(pos); + + QQuick3DModel *hitModel = helper->pickViewAt(viewObj, viewPos.x(), viewPos.y()).objectHit(); + QObject *resolvedPick = helper->resolvePick(hitModel); + + qint32 instanceId = -1; + if (hasInstanceForObject(resolvedPick)) + instanceId = instanceForObject(resolvedPick).instanceId(); + + QVariantList data; + data.append(instanceId); + data.append(QVector3D()); + nodeInstanceClient()->handlePuppetToCreatorCommand({PuppetToCreatorCommand::NodeAtPos, + QVariant::fromValue(data)}); + } +#endif +} + void Qt5InformationNodeInstanceServer::createEditView3D() { #ifdef QUICK3D_MODULE @@ -2283,14 +2319,15 @@ void Qt5InformationNodeInstanceServer::setSceneEnvironmentData( } // Returns list of camera objects to align -// If m_selectedCameras contains cameras, return those +// If m_selectedCameras contains cameras, return those, unless preferCurrentSceneCamera is true // If no cameras have been selected yet, return camera associated with current view3D, if any // If scene is not View3D scene, return first camera in the scene -QVariantList Qt5InformationNodeInstanceServer::alignCameraList() const +QVariantList Qt5InformationNodeInstanceServer::alignCameraList( + [[maybe_unused]] bool preferCurrentSceneCamera) const { QVariantList cameras; #ifdef QUICK3D_MODULE - if (m_selectedCameras.contains(m_active3DScene)) { + if (!preferCurrentSceneCamera && m_selectedCameras.contains(m_active3DScene)) { const QObjectList cameraList = m_selectedCameras[m_active3DScene]; for (const auto camera : cameraList) { if (hasInstanceForObject(camera) && find3DSceneRoot(camera) == m_active3DScene) @@ -2445,10 +2482,14 @@ void Qt5InformationNodeInstanceServer::view3DAction(const View3DActionCommand &c QMetaObject::invokeMethod(m_editView3DData.rootItem, "alignCamerasToView", Q_ARG(QVariant, alignCameraList())); break; - case View3DActionType::AlignViewToCamera: + case View3DActionType::AlignViewToCamera: { + bool preferCurrentSceneCamera = false; + if (!command.value().isNull()) + preferCurrentSceneCamera = command.value().toBool(); QMetaObject::invokeMethod(m_editView3DData.rootItem, "alignViewToCamera", - Q_ARG(QVariant, alignCameraList())); + Q_ARG(QVariant, alignCameraList(preferCurrentSceneCamera))); break; + } case View3DActionType::SelectionModeToggle: updatedToolState.insert("selectionMode", command.isEnabled() ? 1 : 0); break; @@ -2510,6 +2551,12 @@ void Qt5InformationNodeInstanceServer::view3DAction(const View3DActionCommand &c getNodeAtPos(command.value().toPointF()); return; } + case View3DActionType::GetNodeAtMainScenePos: { + QVariantList data = command.value().toList(); + if (data.size() == 2) + getNodeAtMainScenePos(data[0].toPointF(), qint32(data[1].toInt())); + return; + } case View3DActionType::SplitViewToggle: updatedToolState.insert("splitView", command.isEnabled()); break; diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.h b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.h index cb7dd20a96e..d18266dd589 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.h +++ b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.h @@ -128,6 +128,7 @@ private: void updateColorSettings(const QVector &valueChanges); void removeRotationBlocks(const QVector &instanceIds); void getNodeAtPos(const QPointF &pos); + void getNodeAtMainScenePos(const QPointF &pos, qint32 viewId); void createAuxiliaryQuickView(const QUrl &url, RenderViewData &viewData); #ifdef QUICK3D_PARTICLES_MODULE @@ -136,7 +137,7 @@ private: void handleParticleSystemDeselected(); #endif void setSceneEnvironmentData(qint32 instanceId); - QVariantList alignCameraList() const; + QVariantList alignCameraList(bool preferCurrentSceneCamera = false) const; void updateSceneEnvToHelper(); bool isSceneEnvironmentBgProperty(const PropertyName &name) const; From 40821b47677798b3537d8cead39717a91e60f7fc Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Mon, 11 Mar 2024 09:31:36 +0100 Subject: [PATCH 148/176] QmlDesigner: Bump version in project template Change-Id: I6f8c63b56b9a8d6d635a9385b42e714f1e510e02 Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Patch Build Bot --- .../studio_templates/projects/common/app.qmlproject.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/common/app.qmlproject.tpl b/share/qtcreator/qmldesigner/studio_templates/projects/common/app.qmlproject.tpl index 95975c197d1..75aeae67142 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/common/app.qmlproject.tpl +++ b/share/qtcreator/qmldesigner/studio_templates/projects/common/app.qmlproject.tpl @@ -114,7 +114,7 @@ Project { /* Required for deployment */ targetDirectory: "/opt/%{ProjectName}" - qdsVersion: "4.3" + qdsVersion: "4.5" quickVersion: "%{QtQuickVersion}" From 6ebb7923b22a79c65ab3e07e7f2da7558e1fdf87 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 7 Mar 2024 15:49:41 +0100 Subject: [PATCH 149/176] QmlDesigner: Deprecate optionally old node meta info API If the project storage is activated the old API will be deprecated. Hepefully people now stop to use the old API. You can still use it but you have to write an implementation for the new one too. Task-number: QDS-12102 Change-Id: Iac23da1648ff44e27a2ee7840e0d3eeb1fb8caf9 Reviewed-by: Tim Jenssen Reviewed-by: Reviewed-by: Qt CI Patch Build Bot --- .../assetslibrary/assetslibrarywidget.cpp | 6 +- .../components/bindingeditor/actioneditor.cpp | 23 ++- .../bindingeditor/bindingeditor.cpp | 33 ++-- .../components/bindingeditor/signallist.cpp | 14 +- .../collectioneditor/collectionview.cpp | 10 +- .../componentcore/designeractionmanager.cpp | 50 ++++-- .../componentcore/layoutingridlayout.cpp | 10 +- .../componentcore/modelnodeoperations.cpp | 167 +++++++++++------- .../connectioneditor/backendmodel.cpp | 13 +- .../connectioneditor/connectionmodel.cpp | 29 +-- .../qmldesigner/components/createtexture.cpp | 5 +- .../components/debugview/debugview.cpp | 7 +- .../components/edit3d/bakelightsdatamodel.cpp | 4 + .../itemlibrary/itemlibrarymodel.cpp | 5 +- .../materialbrowser/materialutils.cpp | 4 + .../materialeditorqmlbackend.cpp | 2 + .../materialeditor/materialeditorview.cpp | 15 +- .../choosefrompropertylistdialog.cpp | 4 + .../components/navigator/navigatorview.cpp | 10 +- .../propertyeditor/gradientmodel.cpp | 10 +- .../propertyeditorqmlbackend.cpp | 18 +- .../propertyeditor/propertyeditorqmlbackend.h | 11 +- .../propertyeditor/propertyeditorvalue.cpp | 10 ++ .../propertyeditor/propertyeditorview.cpp | 89 +++++----- .../components/stateseditor/propertymodel.cpp | 9 +- .../stateseditor/stateseditormodel.cpp | 4 + .../textureeditor/textureeditorqmlbackend.cpp | 2 + .../textureeditor/textureeditorview.cpp | 19 +- .../timelineeditor/timelineview.cpp | 23 ++- .../transitioneditor/transitioneditorview.cpp | 58 ++++-- .../designercore/include/abstractview.h | 7 + .../qmldesigner/designercore/include/model.h | 14 +- .../designercore/include/nodemetainfo.h | 21 ++- .../designercore/metainfo/nodemetainfo.cpp | 34 ++++ .../designercore/model/abstractview.cpp | 40 ++++- .../qmldesigner/designercore/model/model.cpp | 39 ++-- .../designercore/model/modelmerger.cpp | 8 + .../designercore/model/qmlconnections.cpp | 7 +- .../designercore/model/qmlitemnode.cpp | 22 ++- .../designercore/model/qmlobjectnode.cpp | 2 +- .../designercore/model/qmlstate.cpp | 8 + .../designercore/model/qmlvisualnode.cpp | 20 ++- .../designercore/model/stylesheetmerger.cpp | 10 ++ .../designercore/model/texttomodelmerger.cpp | 31 ++-- .../projectstorage/commontypecache.h | 4 +- .../qmldesigner/qmldesignerprojectmanager.cpp | 26 ++- .../qmldesigner/coretests/tst_testcore.cpp | 43 ++++- 47 files changed, 729 insertions(+), 271 deletions(-) diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp index 067cff57a49..3b98eb6baf9 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp @@ -273,8 +273,11 @@ void AssetsLibraryWidget::setHasSceneEnv(bool b) emit hasSceneEnvChanged(); } -void AssetsLibraryWidget::handleDeleteEffects(const QStringList &effectNames) +void AssetsLibraryWidget::handleDeleteEffects([[maybe_unused]] const QStringList &effectNames) { +#ifdef QDS_USE_PROJECTSTORAGE +// That code has to rewritten with modules. Seem try to find all effects nodes. +#else DesignDocument *document = QmlDesignerPlugin::instance()->currentDesignDocument(); if (!document) return; @@ -340,6 +343,7 @@ void AssetsLibraryWidget::handleDeleteEffects(const QStringList &effectNames) document->clearUndoRedoStacks(); m_assetsView->emitCustomNotification("effectcomposer_effects_deleted", {}, {effectNames}); +#endif } void AssetsLibraryWidget::invalidateThumbnail(const QString &id) diff --git a/src/plugins/qmldesigner/components/bindingeditor/actioneditor.cpp b/src/plugins/qmldesigner/components/bindingeditor/actioneditor.cpp index 5336f6b4dd4..bd63742d5e7 100644 --- a/src/plugins/qmldesigner/components/bindingeditor/actioneditor.cpp +++ b/src/plugins/qmldesigner/components/bindingeditor/actioneditor.cpp @@ -229,6 +229,7 @@ void ActionEditor::prepareConnections() QList singletons; QStringList states; + [[maybe_unused]] auto model = m_modelNode.model(); const QList allNodes = m_modelNode.view()->allModelNodes(); for (const auto &modelNode : allNodes) { // Skip nodes without specified id @@ -240,11 +241,19 @@ void ActionEditor::prepareConnections() for (const auto &property : modelNode.metaInfo().properties()) { if (isSkippedType(property.propertyType())) continue; - +#ifdef QDS_USE_PROJECTSTORAGE + auto exportedTypeName = model->exportedTypeNameForMetaInfo(property.propertyType()) + .name.toQByteArray(); + connection.properties.append( + ActionEditorDialog::PropertyOption(QString::fromUtf8(property.name()), + exportedTypeName, + property.isWritable())); +#else connection.properties.append( ActionEditorDialog::PropertyOption(QString::fromUtf8(property.name()), skipCpp(property.propertyType().typeName()), property.isWritable())); +#endif } for (const VariantProperty &variantProperty : modelNode.variantProperties()) { @@ -305,11 +314,21 @@ void ActionEditor::prepareConnections() for (const auto &property : metaInfo.properties()) { if (isSkippedType(property.propertyType())) continue; - +#ifdef QDS_USE_PROJECTSTORAGE + auto exportedTypeName = model + ->exportedTypeNameForMetaInfo( + property.propertyType()) + .name.toQByteArray(); + singelton.properties.append( + ActionEditorDialog::PropertyOption(QString::fromUtf8(property.name()), + exportedTypeName, + property.isWritable())); +#else singelton.properties.append(ActionEditorDialog::PropertyOption( QString::fromUtf8(property.name()), skipCpp(property.propertyType().typeName()), property.isWritable())); +#endif } if (!singelton.properties.isEmpty()) { diff --git a/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.cpp b/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.cpp index c03c31208fc..6a77b6235eb 100644 --- a/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.cpp +++ b/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.cpp @@ -176,22 +176,22 @@ bool isType(const TypeName &first, const TypeName &second, const Tuple &...types bool compareTypes(const NodeMetaInfo &sourceType, const NodeMetaInfo &targetType) { - if constexpr (useProjectStorage()) { - return targetType.isVariant() || sourceType.isVariant() || targetType == sourceType - || (targetType.isNumber() && sourceType.isNumber()) - || (targetType.isColor() && sourceType.isColor()) - || (targetType.isString() && sourceType.isString()); - } else { - const TypeName source = sourceType.simplifiedTypeName(); - const TypeName target = targetType.simplifiedTypeName(); +#ifdef QDS_USE_PROJECTSTORAGE + return targetType.isVariant() || sourceType.isVariant() || targetType == sourceType + || (targetType.isNumber() && sourceType.isNumber()) + || (targetType.isColor() && sourceType.isColor()) + || (targetType.isString() && sourceType.isString()); +#else + const TypeName source = sourceType.simplifiedTypeName(); + const TypeName target = targetType.simplifiedTypeName(); - static constexpr auto variantTypes = std::make_tuple("alias", "unknown", "variant", "var"); + static constexpr auto variantTypes = std::make_tuple("alias", "unknown", "variant", "var"); - return isType(variantTypes, target) || isType(variantTypes, source) || target == source - || targetType == sourceType || isType(target, source, "double", "real", "int") - || isType(target, source, "QColor", "color") - || isType(target, source, "QString", "string"); - } + return isType(variantTypes, target) || isType(variantTypes, source) || target == source + || targetType == sourceType || isType(target, source, "double", "real", "int") + || isType(target, source, "QColor", "color") + || isType(target, source, "QString", "string"); +#endif } } // namespace @@ -287,8 +287,13 @@ void BindingEditor::updateWindowName() + exportedTypeNames.front().name.toQString() + "]"; } } else { +#ifdef QDS_USE_PROJECTSTORAGE + targetString = " [" + (m_targetName.isEmpty() ? QString() : (m_targetName + ": ")) + + QString::fromUtf8(m_backendValueType.displayName()) + "]"; +#else targetString = " [" + (m_targetName.isEmpty() ? QString() : (m_targetName + ": ")) + QString::fromUtf8(m_backendValueType.simplifiedTypeName()) + "]"; +#endif } m_dialog->setWindowTitle(m_dialog->defaultTitle() + targetString); diff --git a/src/plugins/qmldesigner/components/bindingeditor/signallist.cpp b/src/plugins/qmldesigner/components/bindingeditor/signallist.cpp index 46470682519..12a605e58a0 100644 --- a/src/plugins/qmldesigner/components/bindingeditor/signallist.cpp +++ b/src/plugins/qmldesigner/components/bindingeditor/signallist.cpp @@ -218,14 +218,18 @@ void SignalList::addConnection(const QModelIndex &modelIndex) const ModelNode rootModelNode = view->rootModelNode(); if (rootModelNode.isValid() && rootModelNode.metaInfo().isValid()) { - NodeMetaInfo nodeMetaInfo = view->model()->qtQuickConnectionsMetaInfo(); +#ifndef QDS_USE_PROJECTSTORAGE + NodeMetaInfo nodeMetaInfo = view->model()->qtQmlConnectionsMetaInfo(); if (nodeMetaInfo.isValid()) { - view->executeInTransaction("ConnectionModel::addConnection", - [this, view, nodeMetaInfo, targetModelIndex, modelIndex, - buttonModelIndex, signalName, &rootModelNode] { +#endif + view->executeInTransaction("ConnectionModel::addConnection", [&] { +#ifdef QDS_USE_PROJECTSTORAGE + ModelNode newNode = view->createModelNode("Connections"); +#else ModelNode newNode = view->createModelNode("QtQuick.Connections", nodeMetaInfo.majorVersion(), nodeMetaInfo.minorVersion()); +#endif const QString source = m_modelNode.validId() + ".trigger()"; if (QmlItemNode::isValidQmlItemNode(m_modelNode)) @@ -240,7 +244,9 @@ void SignalList::addConnection(const QModelIndex &modelIndex) m_model->setConnected(modelIndex.row(), true); m_model->setData(buttonModelIndex, newNode.internalId(), SignalListModel::ConnectionsInternalIdRole); }); +#ifndef QDS_USE_PROJECTSTORAGE } +#endif } } diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp index 723d0beca88..93efc36a4fc 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp @@ -29,10 +29,9 @@ namespace { -inline bool isStudioCollectionModel(const QmlDesigner::ModelNode &node) +bool isStudioCollectionModel(const QmlDesigner::ModelNode &node) { - using namespace QmlDesigner::CollectionEditorConstants; - return node.metaInfo().typeName() == JSONCOLLECTIONMODEL_TYPENAME; + return node.metaInfo().isQtQuickStudioUtilsJsonListModel(); } inline void setVariantPropertyValue(const QmlDesigner::ModelNode &node, @@ -201,11 +200,14 @@ void CollectionView::addResource(const QUrl &url, const QString &name) } else { sourceAddress = url.toString(); } - +#ifdef QDS_USE_PROJECTSTORAGE + ModelNode resourceNode = createModelNode("JsonListModel"); +#else const NodeMetaInfo resourceMetaInfo = jsonCollectionMetaInfo(); ModelNode resourceNode = createModelNode(resourceMetaInfo.typeName(), resourceMetaInfo.majorVersion(), resourceMetaInfo.minorVersion()); +#endif VariantProperty sourceProperty = resourceNode.variantProperty( CollectionEditorConstants::SOURCEFILE_PROPERTY); VariantProperty nameProperty = resourceNode.variantProperty("objectName"); diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp index eabe612fda3..27afaa08f2b 100644 --- a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp +++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp @@ -579,10 +579,14 @@ QList getSlotsLists(const ModelNode &node) //creates connection without signalHandlerProperty ModelNode createNewConnection(ModelNode targetNode) { - NodeMetaInfo connectionsMetaInfo = targetNode.view()->model()->qtQuickConnectionsMetaInfo(); +#ifdef QDS_USE_PROJECTSTORAGE + ModelNode newConnectionNode = targetNode.view()->createModelNode("Connections"); +#else + NodeMetaInfo connectionsMetaInfo = targetNode.view()->model()->qtQmlConnectionsMetaInfo(); const auto typeName = useProjectStorage() ? "Connections" : "QtQuick.Connections"; ModelNode newConnectionNode = targetNode.view()->createModelNode( typeName, connectionsMetaInfo.majorVersion(), connectionsMetaInfo.minorVersion()); +#endif if (QmlItemNode::isValidQmlItemNode(targetNode)) { targetNode.nodeAbstractProperty("data").reparentHere(newConnectionNode); } else { @@ -874,20 +878,9 @@ public: NodeMetaInfo modelMetaInfo = view->model()->metaInfo("ListModel"); NodeMetaInfo elementMetaInfo = view->model()->metaInfo("ListElement"); - ListModelEditorModel model{[&] { - return view->createModelNode(useProjectStorage() - ? "ListModel" - : "QtQml.Models.ListModel", - modelMetaInfo.majorVersion(), - modelMetaInfo.minorVersion()); - }, - [&] { - return view->createModelNode( - useProjectStorage() ? "ListElement" - : "QtQml.Models.ListElement", - elementMetaInfo.majorVersion(), - elementMetaInfo.minorVersion()); - }, +#ifdef QDS_USE_PROJECTSTORAGE + ListModelEditorModel model{[&] { return view->createModelNode("ListModel"); }, + [&] { return view->createModelNode("ListElement"); }, [&](const ModelNode &node) { bool isNowInComponent = ModelNodeOperations::goIntoComponent( node); @@ -906,6 +899,33 @@ public: return node; }}; +#else + ListModelEditorModel model{ + [&] { + return view->createModelNode("QtQml.Models.ListModel", + modelMetaInfo.majorVersion(), + modelMetaInfo.minorVersion()); + }, + [&] { + return view->createModelNode("QtQml.Models.ListElement", + elementMetaInfo.majorVersion(), + elementMetaInfo.minorVersion()); + }, + [&](const ModelNode &node) { + bool isNowInComponent = ModelNodeOperations::goIntoComponent(node); + + Model *currentModel = QmlDesignerPlugin::instance()->currentDesignDocument()->currentModel(); + + if (currentModel->rewriterView() && !currentModel->rewriterView()->errors().isEmpty()) { + throw DocumentError{}; + } + + if (isNowInComponent) + return view->rootModelNode(); + + return node; + }}; +#endif model.setListView(targetNode); diff --git a/src/plugins/qmldesigner/components/componentcore/layoutingridlayout.cpp b/src/plugins/qmldesigner/components/componentcore/layoutingridlayout.cpp index a323654b6b5..8d3412e0e85 100644 --- a/src/plugins/qmldesigner/components/componentcore/layoutingridlayout.cpp +++ b/src/plugins/qmldesigner/components/componentcore/layoutingridlayout.cpp @@ -169,9 +169,12 @@ void LayoutInGridLayout::doIt() m_selectionContext.view()->executeInTransaction("LayoutInGridLayout1",[this, &layoutNode, layoutType](){ QTC_ASSERT(m_selectionContext.view()->model()->hasNodeMetaInfo(layoutType), return); - +#ifdef QDS_USE_PROJECTSTORAGE + layoutNode = m_selectionContext.view()->createModelNode(layoutType); +#else NodeMetaInfo metaInfo = m_selectionContext.view()->model()->metaInfo(layoutType); layoutNode = m_selectionContext.view()->createModelNode(layoutType, metaInfo.majorVersion(), metaInfo.minorVersion()); +#endif reparentTo(layoutNode, m_parentNode); }); @@ -387,10 +390,13 @@ void LayoutInGridLayout::fillEmptyCells() if (y > 0) yPos = m_yTopOffsets.at(y-1); +#ifdef QDS_USE_PROJECTSTORAGE + ModelNode newNode = m_selectionContext.view()->createModelNode("Item"); +#else NodeMetaInfo metaInfo = m_selectionContext.view()->model()->metaInfo("QtQuick.Item"); ModelNode newNode = m_selectionContext.view()->createModelNode("QtQuick.Item", metaInfo.majorVersion(), metaInfo.minorVersion()); - +#endif reparentTo(newNode, m_parentNode); m_spacerNodes.append(newNode); diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp index ca7bd21689d..6ed1480d85c 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp @@ -484,11 +484,13 @@ static void layoutHelperFunction(const SelectionContext &selectionContext, selectionContext.view()->executeInTransaction("DesignerActionManager|layoutHelperFunction",[=](){ QmlItemNode parentNode = qmlItemNode.instanceParentItem(); - +#ifdef QDS_USE_PROJECTSTORAGE + const ModelNode layoutNode = selectionContext.view()->createModelNode(layoutType); +#else NodeMetaInfo metaInfo = selectionContext.view()->model()->metaInfo(layoutType); const ModelNode layoutNode = selectionContext.view()->createModelNode(layoutType, metaInfo.majorVersion(), metaInfo.minorVersion()); - +#endif reparentTo(layoutNode, parentNode); QList sortedSelectedNodes = selectionContext.selectedModelNodes(); @@ -886,15 +888,26 @@ void addItemToStackedContainer(const SelectionContext &selectionContext) NodeMetaInfo itemMetaInfo = view->model()->metaInfo("QtQuick.Item", -1, -1); QTC_ASSERT(itemMetaInfo.isValid(), return); - QTC_ASSERT(itemMetaInfo.majorVersion() == 2, return); - +#ifdef QDS_USE_PROJECTSTORAGE + QmlDesigner::ModelNode itemNode = view->createModelNode("Item"); +#else QmlDesigner::ModelNode itemNode = view->createModelNode("QtQuick.Item", itemMetaInfo.majorVersion(), itemMetaInfo.minorVersion()); - +#endif container.defaultNodeListProperty().reparentHere(itemNode); if (potentialTabBar.isValid()) {// The stacked container is hooked up to a TabBar - NodeMetaInfo tabButtonMetaInfo = view->model()->metaInfo("QtQuick.Controls.TabButton", -1, -1); +#ifdef QDS_USE_PROJECTSTORAGE + const int buttonIndex = potentialTabBar.directSubModelNodes().size(); + ModelNode tabButtonNode = view->createModelNode("TabButton"); + + tabButtonNode.variantProperty("text").setValue( + QString::fromLatin1("Tab %1").arg(buttonIndex)); + potentialTabBar.defaultNodeListProperty().reparentHere(tabButtonNode); +#else + NodeMetaInfo tabButtonMetaInfo = view->model()->metaInfo("QtQuick.Controls.TabButton", + -1, + -1); if (tabButtonMetaInfo.isValid()) { const int buttonIndex = potentialTabBar.directSubModelNodes().size(); ModelNode tabButtonNode = @@ -906,6 +919,7 @@ void addItemToStackedContainer(const SelectionContext &selectionContext) potentialTabBar.defaultNodeListProperty().reparentHere(tabButtonNode); } +#endif } }); } @@ -1005,6 +1019,7 @@ void addTabBarToStackedContainer(const SelectionContext &selectionContext) QTC_ASSERT(container.isValid(), return); QTC_ASSERT(container.metaInfo().isValid(), return); +#ifndef QDS_USE_PROJECTSTORAGE NodeMetaInfo tabBarMetaInfo = view->model()->metaInfo("QtQuick.Controls.TabBar", -1, -1); QTC_ASSERT(tabBarMetaInfo.isValid(), return); QTC_ASSERT(tabBarMetaInfo.majorVersion() == 2, return); @@ -1012,6 +1027,7 @@ void addTabBarToStackedContainer(const SelectionContext &selectionContext) NodeMetaInfo tabButtonMetaInfo = view->model()->metaInfo("QtQuick.Controls.TabButton", -1, -1); QTC_ASSERT(tabButtonMetaInfo.isValid(), return); QTC_ASSERT(tabButtonMetaInfo.majorVersion() == 2, return); +#endif QmlItemNode containerItemNode(container); QTC_ASSERT(containerItemNode.isValid(), return); @@ -1019,14 +1035,15 @@ void addTabBarToStackedContainer(const SelectionContext &selectionContext) const PropertyName indexPropertyName = getIndexPropertyName(container); QTC_ASSERT(container.metaInfo().hasProperty(indexPropertyName), return); - view->executeInTransaction("DesignerActionManager:addItemToStackedContainer", - [view, container, containerItemNode, tabBarMetaInfo, tabButtonMetaInfo, indexPropertyName](){ - + view->executeInTransaction("DesignerActionManager:addItemToStackedContainer", [&]() { +#ifdef QDS_USE_PROJECTSTORAGE + ModelNode tabBarNode = view->createModelNode("TabBar"); +#else ModelNode tabBarNode = view->createModelNode("QtQuick.Controls.TabBar", tabBarMetaInfo.majorVersion(), tabBarMetaInfo.minorVersion()); - +#endif container.parentProperty().reparentHere(tabBarNode); const int maxValue = container.directSubModelNodes().size(); @@ -1038,11 +1055,14 @@ void addTabBarToStackedContainer(const SelectionContext &selectionContext) tabBarItem.anchors().setAnchor(AnchorLineBottom, containerItemNode, AnchorLineTop); for (int i = 0; i < maxValue; ++i) { +#ifdef QDS_USE_PROJECTSTORAGE + ModelNode tabButtonNode = view->createModelNode("TabButton"); +#else ModelNode tabButtonNode = view->createModelNode("QtQuick.Controls.TabButton", tabButtonMetaInfo.majorVersion(), tabButtonMetaInfo.minorVersion()); - +#endif tabButtonNode.variantProperty("text").setValue(QString::fromLatin1("Tab %1").arg(i)); tabBarNode.defaultNodeListProperty().reparentHere(tabButtonNode); } @@ -1053,7 +1073,6 @@ void addTabBarToStackedContainer(const SelectionContext &selectionContext) const QString expression = id + "." + QString::fromLatin1(indexPropertyName); container.bindingProperty(indexPropertyName).setExpression(expression); }); - } AddFilesResult addFilesToProject(const QStringList &fileNames, const QString &defaultDir, bool showDialog) @@ -1170,23 +1189,22 @@ void createFlowActionArea(const SelectionContext &selectionContext) const QPointF pos = selectionContext.scenePosition().isNull() ? QPointF() : selectionContext.scenePosition() - QmlItemNode(container).flowPosition(); - view->executeInTransaction("DesignerActionManager:createFlowActionArea", - [view, container, actionAreaMetaInfo, pos](){ - - ModelNode flowActionNode = - view->createModelNode("FlowView.FlowActionArea", + view->executeInTransaction("DesignerActionManager:createFlowActionArea", [&]() { +#ifdef QDS_USE_PROJECTSTORAGE + ModelNode flowActionNode = view->createModelNode("FlowActionArea"); +#else + ModelNode flowActionNode = view->createModelNode("FlowView.FlowActionArea", actionAreaMetaInfo.majorVersion(), actionAreaMetaInfo.minorVersion()); +#endif + if (!pos.isNull()) { + flowActionNode.variantProperty("x").setValue(pos.x()); + flowActionNode.variantProperty("y").setValue(pos.y()); + } - if (!pos.isNull()) { - flowActionNode.variantProperty("x").setValue(pos.x()); - flowActionNode.variantProperty("y").setValue(pos.y()); - } - - container.defaultNodeListProperty().reparentHere(flowActionNode); - view->setSelectedModelNode(flowActionNode); - }); - + container.defaultNodeListProperty().reparentHere(flowActionNode); + view->setSelectedModelNode(flowActionNode); + }); } void addTransition(const SelectionContext &selectionContext) @@ -1221,17 +1239,18 @@ void addFlowEffect(const SelectionContext &selectionContext, const TypeName &typ NodeMetaInfo effectMetaInfo = view->model()->metaInfo("FlowView." + typeName, -1, -1); QTC_ASSERT(typeName == "None" || effectMetaInfo.isValid(), return); - view->executeInTransaction("DesignerActionManager:addFlowEffect", [=]() { + view->executeInTransaction("DesignerActionManager:addFlowEffect", [&]() { if (container.hasProperty("effect")) container.removeProperty("effect"); if (effectMetaInfo.isQtObject()) { - ModelNode effectNode = view->createModelNode(useProjectStorage() - ? typeName - : effectMetaInfo.typeName(), +#ifdef QDS_USE_PROJECTSTORAGE + ModelNode effectNode = view->createModelNode(typeName); +#else + ModelNode effectNode = view->createModelNode(effectMetaInfo.typeName(), effectMetaInfo.majorVersion(), effectMetaInfo.minorVersion()); - +#endif container.nodeProperty("effect").reparentHere(effectNode); view->setSelectedModelNode(effectNode); } @@ -1299,7 +1318,6 @@ void reparentToNodeAndAdjustPosition(const ModelNode &parentModelNode, void addToGroupItem(const SelectionContext &selectionContext) { - const TypeName typeName = "QtQuick.Studio.Components.GroupItem"; try { if (!hasStudioComponentsImport(selectionContext)) { @@ -1315,20 +1333,28 @@ void addToGroupItem(const SelectionContext &selectionContext) if (qmlItemNode.hasInstanceParentItem()) { ModelNode groupNode; - selectionContext.view()->executeInTransaction("DesignerActionManager|addToGroupItem1",[=, &groupNode](){ + selectionContext.view() + ->executeInTransaction("DesignerActionManager|addToGroupItem1", [&]() { + QmlItemNode parentNode = qmlItemNode.instanceParentItem(); +#ifdef QDS_USE_PROJECTSTORAGE + groupNode = selectionContext.view()->createModelNode("GroupItem"); +#else + const TypeName typeName = "QtQuick.Studio.Components.GroupItem"; - QmlItemNode parentNode = qmlItemNode.instanceParentItem(); - NodeMetaInfo metaInfo = selectionContext.view()->model()->metaInfo(typeName); - groupNode = selectionContext.view()->createModelNode(typeName, metaInfo.majorVersion(), metaInfo.minorVersion()); - reparentTo(groupNode, parentNode); - }); - selectionContext.view()->executeInTransaction("DesignerActionManager|addToGroupItem2",[=](){ + NodeMetaInfo metaInfo = selectionContext.view()->model()->metaInfo(typeName); + groupNode = selectionContext.view()->createModelNode(typeName, + metaInfo.majorVersion(), + metaInfo.minorVersion()); +#endif + reparentTo(groupNode, parentNode); + }); + selectionContext.view() + ->executeInTransaction("DesignerActionManager|addToGroupItem2", [&]() { + QList selectedNodes = selectionContext.selectedModelNodes(); + setUpperLeftPostionToNode(groupNode, selectedNodes); - QList selectedNodes = selectionContext.selectedModelNodes(); - setUpperLeftPostionToNode(groupNode, selectedNodes); - - reparentToNodeAndAdjustPosition(groupNode, selectedNodes); - }); + reparentToNodeAndAdjustPosition(groupNode, selectedNodes); + }); } } } catch (RewritingException &e) { @@ -1387,7 +1413,6 @@ static void getTypeAndImport(const SelectionContext &selectionContext, void addCustomFlowEffect(const SelectionContext &selectionContext) { - TypeName typeName; QString typeString; @@ -1416,23 +1441,27 @@ void addCustomFlowEffect(const SelectionContext &selectionContext) QTC_ASSERT(container.metaInfo().isValid(), return); QTC_ASSERT(QmlItemNode::isFlowTransition(container), return); +#ifndef QDS_USE_PROJECTSTORAGE NodeMetaInfo effectMetaInfo = view->model()->metaInfo(typeName, -1, -1); QTC_ASSERT(typeName == "None" || effectMetaInfo.isValid(), return); - - view->executeInTransaction("DesignerActionManager:addFlowEffect", [=]() { +#endif + view->executeInTransaction("DesignerActionManager:addFlowEffect", [&]() { if (container.hasProperty("effect")) container.removeProperty("effect"); +#ifdef QDS_USE_PROJECTSTORAGE + ModelNode effectNode = view->createModelNode(typeName); + container.nodeProperty("effect").reparentHere(effectNode); + view->setSelectedModelNode(effectNode); +#else if (effectMetaInfo.isValid()) { - ModelNode effectNode = view->createModelNode(useProjectStorage() - ? typeName - : effectMetaInfo.typeName(), + ModelNode effectNode = view->createModelNode(effectMetaInfo.typeName(), effectMetaInfo.majorVersion(), effectMetaInfo.minorVersion()); - container.nodeProperty("effect").reparentHere(effectNode); view->setSelectedModelNode(effectNode); } +#endif }); } @@ -1450,7 +1479,6 @@ static QString fromCamelCase(const QString &s) QString getTemplateDialog(const Utils::FilePath &projectPath) { - const Utils::FilePath templatesPath = projectPath.pathAppended("templates"); const QStringList templateFiles = QDir(templatesPath.toString()).entryList({"*.qml"}); @@ -1598,23 +1626,30 @@ void addMouseAreaFill(const SelectionContext &selectionContext) return; } - selectionContext.view()->executeInTransaction("DesignerActionManager|addMouseAreaFill", [selectionContext]() { - ModelNode modelNode = selectionContext.currentSingleSelectedNode(); - if (modelNode.isValid()) { - NodeMetaInfo itemMetaInfo = selectionContext.view()->model()->metaInfo("QtQuick.MouseArea", -1, -1); - QTC_ASSERT(itemMetaInfo.isValid(), return); + selectionContext.view() + ->executeInTransaction("DesignerActionManager|addMouseAreaFill", [selectionContext]() { + ModelNode modelNode = selectionContext.currentSingleSelectedNode(); + if (modelNode.isValid()) { +#ifdef QDS_USE_PROJECTSTORAGE + QmlDesigner::ModelNode mouseAreaNode = selectionContext.view()->createModelNode( + "MouseArea"); +#else + NodeMetaInfo itemMetaInfo = selectionContext.view()->model()->metaInfo( + "QtQuick.MouseArea", -1, -1); + QTC_ASSERT(itemMetaInfo.isValid(), return); - QmlDesigner::ModelNode mouseAreaNode = - selectionContext.view()->createModelNode("QtQuick.MouseArea", itemMetaInfo.majorVersion(), itemMetaInfo.minorVersion()); - mouseAreaNode.validId(); + QmlDesigner::ModelNode mouseAreaNode = selectionContext.view()->createModelNode( + "QtQuick.MouseArea", itemMetaInfo.majorVersion(), itemMetaInfo.minorVersion()); +#endif + mouseAreaNode.validId(); - modelNode.defaultNodeListProperty().reparentHere(mouseAreaNode); - QmlItemNode mouseAreaItemNode(mouseAreaNode); - if (mouseAreaItemNode.isValid()) { - mouseAreaItemNode.anchors().fill(); + modelNode.defaultNodeListProperty().reparentHere(mouseAreaNode); + QmlItemNode mouseAreaItemNode(mouseAreaNode); + if (mouseAreaItemNode.isValid()) { + mouseAreaItemNode.anchors().fill(); + } } - } - }); + }); } QVariant previewImageDataForGenericNode(const ModelNode &modelNode) diff --git a/src/plugins/qmldesigner/components/connectioneditor/backendmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/backendmodel.cpp index 5d9e9fe99c6..97c73205b29 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/backendmodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/backendmodel.cpp @@ -228,16 +228,17 @@ void BackendModel::addNewBackend() if (!dialog.isSingleton()) { m_connectionView->executeInTransaction("BackendModel::addNewBackend", [this, metaInfo, typeName, propertyName, &dialog] { - int minorVersion = metaInfo.minorVersion(); - int majorVersion = metaInfo.majorVersion(); if (dialog.localDefinition()) { - ModelNode newNode = m_connectionView->createModelNode(useProjectStorage() - ? typeName.toUtf8() - : metaInfo.typeName(), +#ifdef QDS_USE_PROJECTSTORAGE + ModelNode newNode = m_connectionView->createModelNode(typeName.toUtf8()); +#else + int minorVersion = metaInfo.minorVersion(); + int majorVersion = metaInfo.majorVersion(); + ModelNode newNode = m_connectionView->createModelNode(metaInfo.typeName(), majorVersion, minorVersion); - +#endif m_connectionView->rootModelNode().nodeProperty(propertyName.toUtf8()).setDynamicTypeNameAndsetModelNode( typeName.toUtf8(), newNode); } else { diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp index d3fcd019fbd..2047f7625c1 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp @@ -48,7 +48,7 @@ QStringList propertyNameListToStringList(const QmlDesigner::PropertyNameList &pr bool isConnection(const QmlDesigner::ModelNode &modelNode) { - return (modelNode.metaInfo().simplifiedTypeName() == "Connections"); + return modelNode.metaInfo().isQtQmlConnections(); } } //namespace @@ -361,15 +361,14 @@ void ConnectionModel::addConnection(const PropertyName &signalName) ModelNode rootModelNode = connectionView()->rootModelNode(); if (rootModelNode.isValid() && rootModelNode.metaInfo().isValid()) { - - NodeMetaInfo nodeMetaInfo = connectionView()->model()->qtQuickConnectionsMetaInfo(); +#ifndef QDS_USE_PROJECTSTORAGE + NodeMetaInfo nodeMetaInfo = connectionView()->model()->qtQmlConnectionsMetaInfo(); if (nodeMetaInfo.isValid()) { - ModelNode selectedNode; - if (connectionView()->selectedModelNodes().isEmpty()) +#endif + ModelNode selectedNode = connectionView()->firstSelectedModelNode(); + if (!selectedNode) selectedNode = connectionView()->rootModelNode(); - else - selectedNode = connectionView()->selectedModelNodes().constFirst(); PropertyName signalHandlerName = signalName; if (signalHandlerName.isEmpty()) @@ -378,12 +377,14 @@ void ConnectionModel::addConnection(const PropertyName &signalName) signalHandlerName = addOnToSignalName(QString::fromUtf8(signalHandlerName)).toUtf8(); connectionView() - ->executeInTransaction("ConnectionModel::addConnection", - [this, nodeMetaInfo, signalHandlerName, &rootModelNode] { - ModelNode newNode = connectionView() - ->createModelNode("QtQuick.Connections", - nodeMetaInfo.majorVersion(), - nodeMetaInfo.minorVersion()); + ->executeInTransaction("ConnectionModel::addConnection", [&] { +#ifdef QDS_USE_PROJECTSTORAGE + ModelNode newNode = connectionView()->createModelNode("Connections"); +#else + ModelNode newNode = connectionView()->createModelNode("QtQuick.Connections", + nodeMetaInfo.majorVersion(), + nodeMetaInfo.minorVersion()); +#endif QString source = "console.log(\"clicked\")"; if (connectionView()->selectedModelNodes().size() == 1) { @@ -411,7 +412,9 @@ void ConnectionModel::addConnection(const PropertyName &signalName) selectProperty(newNode.signalHandlerProperty(signalHandlerName)); }); +#ifndef QDS_USE_PROJECTSTORAGE } +#endif } } diff --git a/src/plugins/qmldesigner/components/createtexture.cpp b/src/plugins/qmldesigner/components/createtexture.cpp index 1363868f37f..b6e99ae972c 100644 --- a/src/plugins/qmldesigner/components/createtexture.cpp +++ b/src/plugins/qmldesigner/components/createtexture.cpp @@ -84,10 +84,13 @@ ModelNode CreateTexture::createTextureFromImage(const Utils::FilePath &assetPat ModelNode newTexNode = Utils3D::getTextureDefaultInstance(textureSource, m_view); if (!newTexNode.isValid()) { +#ifdef QDS_USE_PROJECTSTORAGE + newTexNode = m_view->createModelNode("Texture"); +#else newTexNode = m_view->createModelNode("QtQuick3D.Texture", metaInfo.majorVersion(), metaInfo.minorVersion()); - +#endif newTexNode.setIdWithoutRefactoring(m_view->model()->generateNewId(assetPath.baseName())); VariantProperty sourceProp = newTexNode.variantProperty("source"); diff --git a/src/plugins/qmldesigner/components/debugview/debugview.cpp b/src/plugins/qmldesigner/components/debugview/debugview.cpp index 64f85915f16..e05001e4774 100644 --- a/src/plugins/qmldesigner/components/debugview/debugview.cpp +++ b/src/plugins/qmldesigner/components/debugview/debugview.cpp @@ -95,7 +95,9 @@ void DebugView::nodeCreated(const ModelNode &createdNode) message << createdNode.nodeSource(); message << "MetaInfo " << createdNode.metaInfo().isValid() << " "; if (auto metaInfo = createdNode.metaInfo()) { +#ifndef QDS_USE_PROJECTSTORAGE message << metaInfo.majorVersion() << "." << metaInfo.minorVersion(); +#endif message << ModelUtils::componentFilePath(createdNode); } log("::nodeCreated:", message.readAll()); @@ -283,6 +285,7 @@ void DebugView::selectedNodesChanged(const QList &selectedNodes /*sel message << lineBreak; if (selectedNode.metaInfo().isValid()) { +#ifndef QDS_USE_PROJECTSTORAGE for (const NodeMetaInfo &metaInfo : selectedNode.metaInfo().selfAndPrototypes()) { message << metaInfo.typeName() << " " << metaInfo.majorVersion() << "." << metaInfo.minorVersion() << lineBreak; @@ -290,6 +293,7 @@ void DebugView::selectedNodesChanged(const QList &selectedNodes /*sel message << lineBreak; message << selectedNode.metaInfo().typeName(); +#endif message << lineBreak; message << "Node Source" << selectedNode.nodeSource(); @@ -323,10 +327,11 @@ void DebugView::selectedNodesChanged(const QList &selectedNodes /*sel message << "Is valid object node: " << QmlItemNode::isValidQmlObjectNode(selectedNode); message << lineBreak; +#ifndef QDS_USE_PROJECTSTORAGE message << "version: " << QString::number(selectedNode.metaInfo().majorVersion()) + "." + QString::number(selectedNode.metaInfo().minorVersion()); - +#endif message << lineBreak; QmlItemNode itemNode(selectedNode); diff --git a/src/plugins/qmldesigner/components/edit3d/bakelightsdatamodel.cpp b/src/plugins/qmldesigner/components/edit3d/bakelightsdatamodel.cpp index e812bb6d00e..9657d564fb2 100644 --- a/src/plugins/qmldesigner/components/edit3d/bakelightsdatamodel.cpp +++ b/src/plugins/qmldesigner/components/edit3d/bakelightsdatamodel.cpp @@ -353,10 +353,14 @@ void BakeLightsDataModel::apply() if (node.hasBindingProperty(propName)) blmNode = node.bindingProperty(propName).resolveToModelNode(); if (!blmNode.isValid() && data.enabled) { +#ifdef QDS_USE_PROJECTSTORAGE + blmNode = m_view->createModelNode("BakedLightmap"); +#else NodeMetaInfo metaInfo = m_view->model()->qtQuick3DBakedLightmapMetaInfo(); blmNode = m_view->createModelNode("QtQuick3D.BakedLightmap", metaInfo.majorVersion(), metaInfo.minorVersion()); +#endif QString idPart; if (data.aliasProp.isEmpty()) idPart = data.id; diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp index eaf6f89b14a..a0fec7e507a 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp @@ -371,10 +371,13 @@ void ItemLibraryModel::update([[maybe_unused]] ItemLibraryInfo *itemLibraryInfo, else metaInfo = model->metaInfo(entry.typeName()); +#ifdef QDS_USE_PROJECTSTORAGE + bool valid = metaInfo.isValid(); +#else bool valid = metaInfo.isValid() && (metaInfo.majorVersion() >= entry.majorVersion() || metaInfo.majorVersion() < 0); - +#endif bool isItem = valid && metaInfo.isQtQuickItem(); bool forceVisibility = valid && NodeHints::fromItemLibraryEntry(entry).visibleInLibrary(); diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialutils.cpp b/src/plugins/qmldesigner/components/materialbrowser/materialutils.cpp index f22fdc030e4..1ee2e409f38 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialutils.cpp +++ b/src/plugins/qmldesigner/components/materialbrowser/materialutils.cpp @@ -47,10 +47,14 @@ void MaterialUtils::assignMaterialTo3dModel(AbstractView *view, const ModelNode // if no valid material, create a new default material if (!newMaterialNode.isValid()) { +#ifdef QDS_USE_PROJECTSTORAGE + newMaterialNode = view->createModelNode("PrincipledMaterial"); +#else NodeMetaInfo metaInfo = view->model()->qtQuick3DPrincipledMaterialMetaInfo(); newMaterialNode = view->createModelNode("QtQuick3D.PrincipledMaterial", metaInfo.majorVersion(), metaInfo.minorVersion()); +#endif newMaterialNode.validId(); } } diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.cpp b/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.cpp index 661f1ad3788..ecc460ae510 100644 --- a/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.cpp +++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.cpp @@ -287,8 +287,10 @@ void MaterialEditorQmlBackend::setup(const QmlObjectNode &selectedMaterialNode, contextObject()->setSelectionChanged(false); +#ifndef QDS_USE_PROJECTSTORAGE NodeMetaInfo metaInfo = selectedMaterialNode.modelNode().metaInfo(); contextObject()->setMajorVersion(metaInfo.isValid() ? metaInfo.majorVersion() : -1); +#endif } else { context()->setContextProperty("hasMaterial", QVariant(false)); } diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp index ab43e992f7a..e083310cdbb 100644 --- a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp +++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp @@ -419,11 +419,14 @@ void MaterialEditorView::handleToolBarAction(int action) ModelNode matLib = Utils3D::materialLibraryNode(this); if (!matLib.isValid()) return; - +#ifdef QDS_USE_PROJECTSTORAGE + ModelNode newMatNode = createModelNode("PrincipledMaterial"); +#else NodeMetaInfo metaInfo = model()->qtQuick3DPrincipledMaterialMetaInfo(); ModelNode newMatNode = createModelNode("QtQuick3D.PrincipledMaterial", metaInfo.majorVersion(), metaInfo.minorVersion()); +#endif renameMaterial(newMatNode, "New Material"); matLib.defaultNodeListProperty().reparentHere(newMatNode); }); @@ -526,6 +529,10 @@ void MaterialEditorView::handlePreviewModelChanged(const QString &modelStr) void MaterialEditorView::setupQmlBackend() { +#ifdef QDS_USE_PROJECTSTORAGE +// TODO unify implementation with property editor view +#else + QUrl qmlPaneUrl; QUrl qmlSpecificsUrl; QString specificQmlData; @@ -603,6 +610,7 @@ void MaterialEditorView::setupQmlBackend() m_stackedWidget->setCurrentWidget(m_qmlBackEnd->widget()); m_stackedWidget->setMinimumSize({400, 300}); +#endif } void MaterialEditorView::commitVariantValueToModel(const PropertyName &propertyName, const QVariant &value) @@ -1037,9 +1045,12 @@ void MaterialEditorView::duplicateMaterial(const ModelNode &material) return; // create the duplicate material +#ifdef QDS_USE_PROJECTSTORAGE + QmlObjectNode duplicateMat = createModelNode(matType); +#else NodeMetaInfo metaInfo = model()->metaInfo(matType); QmlObjectNode duplicateMat = createModelNode(matType, metaInfo.majorVersion(), metaInfo.minorVersion()); - +#endif duplicateMatNode = duplicateMat.modelNode(); // set name and id diff --git a/src/plugins/qmldesigner/components/navigator/choosefrompropertylistdialog.cpp b/src/plugins/qmldesigner/components/navigator/choosefrompropertylistdialog.cpp index 04c2ebd9e65..58b4c427499 100644 --- a/src/plugins/qmldesigner/components/navigator/choosefrompropertylistdialog.cpp +++ b/src/plugins/qmldesigner/components/navigator/choosefrompropertylistdialog.cpp @@ -97,9 +97,13 @@ ChooseFromPropertyListFilter::ChooseFromPropertyListFilter(const NodeMetaInfo &i } else if (insertInfo.isQtQuick3DMaterial()) { if (parentInfo.isQtQuick3DModel()) propertyList.append("materials"); +#ifdef QDS_USE_PROJECTSTORAGE +// TODO add the types here or use the module +#else } else if (insertInfo.typeName().startsWith("ComponentBundles.MaterialBundle")) { if (parentInfo.isQtQuick3DModel()) propertyList.append("materials"); +#endif } else if (insertInfo.isQtQuick3DBakedLightmap()) { if (parentInfo.isQtQuick3DModel()) propertyList.append("bakedLightmap"); diff --git a/src/plugins/qmldesigner/components/navigator/navigatorview.cpp b/src/plugins/qmldesigner/components/navigator/navigatorview.cpp index a3ad444e243..090beb64717 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatorview.cpp +++ b/src/plugins/qmldesigner/components/navigator/navigatorview.cpp @@ -271,14 +271,20 @@ void NavigatorView::dragStarted(QMimeData *mimeData) } else if (mimeData->hasFormat(Constants::MIME_TYPE_TEXTURE)) { qint32 internalId = mimeData->data(Constants::MIME_TYPE_TEXTURE).toInt(); ModelNode texNode = modelNodeForInternalId(internalId); - +#ifdef QDS_USE_PROJECTSTORAGE + m_widget->setDragType(texNode.type()); +#else m_widget->setDragType(texNode.metaInfo().typeName()); +#endif m_widget->update(); } else if (mimeData->hasFormat(Constants::MIME_TYPE_MATERIAL)) { qint32 internalId = mimeData->data(Constants::MIME_TYPE_MATERIAL).toInt(); ModelNode matNode = modelNodeForInternalId(internalId); - +#ifdef QDS_USE_PROJECTSTORAGE + m_widget->setDragType(matNode.type()); +#else m_widget->setDragType(matNode.metaInfo().typeName()); +#endif m_widget->update(); } else if (mimeData->hasFormat(Constants::MIME_TYPE_BUNDLE_TEXTURE)) { m_widget->setDragType(Constants::MIME_TYPE_BUNDLE_TEXTURE); diff --git a/src/plugins/qmldesigner/components/propertyeditor/gradientmodel.cpp b/src/plugins/qmldesigner/components/propertyeditor/gradientmodel.cpp index 53af36c7ae1..8bedabd83a7 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/gradientmodel.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/gradientmodel.cpp @@ -695,6 +695,10 @@ void GradientModel::resetPuppet() QmlDesigner::ModelNode GradientModel::createGradientNode() { +#ifdef QDS_USE_PROJECTSTORAGE + QmlDesigner::TypeName typeName = m_gradientTypeName.toUtf8(); + auto gradientNode = view()->createModelNode(typeName); +#else QmlDesigner::TypeName fullTypeName = m_gradientTypeName.toUtf8(); if (m_gradientTypeName == "Gradient") { @@ -709,7 +713,7 @@ QmlDesigner::ModelNode GradientModel::createGradientNode() int majorVersion = metaInfo.majorVersion(); auto gradientNode = view()->createModelNode(fullTypeName, majorVersion, minorVersion); - +#endif setupGradientProperties(gradientNode); return gradientNode; @@ -717,6 +721,9 @@ QmlDesigner::ModelNode GradientModel::createGradientNode() QmlDesigner::ModelNode GradientModel::createGradientStopNode() { +#ifdef QDS_USE_PROJECTSTORAGE + return view()->createModelNode("GradientStop"); +#else QByteArray fullTypeName = "QtQuick.GradientStop"; auto metaInfo = model()->metaInfo(fullTypeName); @@ -724,6 +731,7 @@ QmlDesigner::ModelNode GradientModel::createGradientStopNode() int majorVersion = metaInfo.majorVersion(); return view()->createModelNode(fullTypeName, majorVersion, minorVersion); +#endif } void GradientModel::deleteGradientNode(bool saveTransaction) diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp index 757777d656c..7f1ab00bb94 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp @@ -10,11 +10,12 @@ #include #include #include -#include +#include #include #include #include #include +#include #include @@ -514,6 +515,12 @@ void PropertyEditorQmlBackend::setup(const QmlObjectNode &qmlObjectNode, const Q NodeMetaInfo metaInfo = qmlObjectNode.modelNode().metaInfo(); +#ifdef QDS_USE_PROJECTSTORAGE + contextObject()->setMajorVersion(-1); + contextObject()->setMinorVersion(-1); + contextObject()->setMajorQtQuickVersion(-1); + contextObject()->setMinorQtQuickVersion(-1); +#else if (metaInfo.isValid()) { contextObject()->setMajorVersion(metaInfo.majorVersion()); contextObject()->setMinorVersion(metaInfo.minorVersion()); @@ -523,7 +530,7 @@ void PropertyEditorQmlBackend::setup(const QmlObjectNode &qmlObjectNode, const Q contextObject()->setMajorQtQuickVersion(-1); contextObject()->setMinorQtQuickVersion(-1); } - +#endif contextObject()->setMajorQtQuickVersion(qmlObjectNode.view()->majorQtQuickVersion()); contextObject()->setMinorQtQuickVersion(qmlObjectNode.view()->minorQtQuickVersion()); @@ -619,6 +626,7 @@ inline bool dotPropertyHeuristic(const QmlObjectNode &node, const NodeMetaInfo & return true; } +#ifndef QDS_USE_PROJECTSTORAGE QString PropertyEditorQmlBackend::templateGeneration(const NodeMetaInfo &metaType, const NodeMetaInfo &superType, const QmlObjectNode &node) @@ -849,6 +857,7 @@ QUrl PropertyEditorQmlBackend::getQmlFileUrl(const TypeName &relativeTypeName, c { return fileToUrl(locateQmlFile(info, QString::fromUtf8(fixTypeNameForPanes(relativeTypeName) + ".qml"))); } +#endif // QDS_USE_PROJECTSTORAGE TypeName PropertyEditorQmlBackend::fixTypeNameForPanes(const TypeName &typeName) { @@ -883,11 +892,13 @@ NodeMetaInfo PropertyEditorQmlBackend::findCommonAncestor(const ModelNode &node) return node.metaInfo(); } +#ifndef QDS_USE_PROJECTSTORAGE TypeName PropertyEditorQmlBackend::qmlFileName(const NodeMetaInfo &nodeInfo) { const TypeName fixedTypeName = fixTypeNameForPanes(nodeInfo.typeName()); return fixedTypeName + "Pane.qml"; } +#endif QUrl PropertyEditorQmlBackend::fileToUrl(const QString &filePath) { QUrl fileUrl; @@ -963,6 +974,7 @@ void PropertyEditorQmlBackend::setValueforAuxiliaryProperties(const QmlObjectNod setValue(qmlObjectNode, propertyName, qmlObjectNode.modelNode().auxiliaryDataWithDefault(key)); } +#ifndef QDS_USE_PROJECTSTORAGE std::tuple PropertyEditorQmlBackend::getQmlUrlForMetaInfo(const NodeMetaInfo &metaInfo) { QString className; @@ -1026,7 +1038,7 @@ QString PropertyEditorQmlBackend::locateQmlFile(const NodeMetaInfo &info, const return QFileInfo::exists(possibleFilePath); }); } - +#endif // QDS_USE_PROJECTSTORAGE } //QmlDesigner diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h index 64cca972071..b677258488b 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h @@ -48,10 +48,13 @@ public: PropertyEditorValue *propertyValueForName(const QString &propertyName); static QString propertyEditorResourcesPath(); - static QString templateGeneration(const NodeMetaInfo &type, const NodeMetaInfo &superType, const QmlObjectNode &node); - +#ifndef QDS_USE_PROJECTSTORAGE + static QString templateGeneration(const NodeMetaInfo &type, + const NodeMetaInfo &superType, + const QmlObjectNode &node); static QUrl getQmlFileUrl(const TypeName &relativeTypeName, const NodeMetaInfo &info); static std::tuple getQmlUrlForMetaInfo(const NodeMetaInfo &modelNode); +#endif static bool checkIfUrlExists(const QUrl &url); @@ -80,10 +83,12 @@ private: PropertyEditorView *propertyEditor, const NodeMetaInfo &type); - static TypeName qmlFileName(const NodeMetaInfo &nodeInfo); static QUrl fileToUrl(const QString &filePath); static QString fileFromUrl(const QUrl &url); +#ifndef QDS_USE_PROJECTSTORAGE + static TypeName qmlFileName(const NodeMetaInfo &nodeInfo); static QString locateQmlFile(const NodeMetaInfo &info, const QString &relativePath); +#endif static TypeName fixTypeNameForPanes(const TypeName &typeName); private: diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp index f042df5241b..663ebafb659 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp @@ -579,6 +579,15 @@ void PropertyEditorNodeWrapper::add(const QString &type) TypeName propertyType = type.toUtf8(); if ((m_editorValue && m_editorValue->modelNode().isValid())) { +#ifdef QDS_USE_PROJECTSTORAGE + if (propertyType.isEmpty()) { + auto node = m_editorValue->modelNode(); + auto metaInfo = node.metaInfo().property(m_editorValue->name()).propertyType(); + auto exportedTypeName = node.model()->exportedTypeNameForMetaInfo(metaInfo); + propertyType = exportedTypeName.name.toQByteArray(); + } + m_modelNode = m_editorValue->modelNode().view()->createModelNode(propertyType); +#else if (propertyType.isEmpty()) { propertyType = m_editorValue->modelNode() .metaInfo() @@ -589,6 +598,7 @@ void PropertyEditorNodeWrapper::add(const QString &type) while (propertyType.contains('*')) // strip star propertyType.chop(1); m_modelNode = m_editorValue->modelNode().view()->createModelNode(propertyType, 4, 7); +#endif m_editorValue->modelNode().nodeAbstractProperty(m_editorValue->name()).reparentHere(m_modelNode); if (!m_modelNode.isValid()) qWarning("PropertyEditorNodeWrapper::add failed"); diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp index 98901682a5e..1ff098f4eac 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp @@ -96,8 +96,9 @@ PropertyEditorView::~PropertyEditorView() qDeleteAll(m_qmlBackendHash); } -void PropertyEditorView::setupPane(const TypeName &typeName) +void PropertyEditorView::setupPane([[maybe_unused]] const TypeName &typeName) { +#ifndef QDS_USE_PROJECTSTORAGE NodeMetaInfo metaInfo = model()->metaInfo(typeName); QUrl qmlFile = PropertyEditorQmlBackend::getQmlFileUrl("Qt/ItemPane", metaInfo); @@ -118,6 +119,7 @@ void PropertyEditorView::setupPane(const TypeName &typeName) } else { qmlBackend->initialSetup(typeName, qmlSpecificsFile, this); } +#endif // QDS_USE_PROJECTSTORAGE } void PropertyEditorView::changeValue(const QString &name) @@ -448,6 +450,7 @@ void PropertyEditorView::resetView() namespace { +#ifndef QDS_USE_PROJECTSTORAGE [[maybe_unused]] std::tuple diffType(const NodeMetaInfo &commonAncestor, const NodeMetaInfo &specificsClassMetaInfo) { @@ -483,6 +486,7 @@ namespace { return {}; } +#endif // QDS_USE_PROJECTSTORAGE PropertyEditorQmlBackend *getQmlBackend(QHash &qmlBackendHash, const QUrl &qmlFileUrl, @@ -572,63 +576,56 @@ void setupWidget(PropertyEditorQmlBackend *currentQmlBackend, void PropertyEditorView::setupQmlBackend() { - if constexpr (useProjectStorage()) { - auto selfAndPrototypes = m_selectedNode.metaInfo().selfAndPrototypes(); - bool isEditableComponent = m_selectedNode.isComponent() - && !QmlItemNode(m_selectedNode).isEffectItem(); - auto specificQmlData = m_propertyEditorComponentGenerator.create(selfAndPrototypes, - isEditableComponent); - auto [panePath, specificsPath] = findPaneAndSpecificsPath(selfAndPrototypes, - model()->pathCache()); - PropertyEditorQmlBackend *currentQmlBackend = getQmlBackend(m_qmlBackendHash, - QUrl::fromLocalFile( - QString{panePath}), - m_imageCache, - m_stackedWidget, - this); +#ifdef QDS_USE_PROJECTSTORAGE + auto selfAndPrototypes = m_selectedNode.metaInfo().selfAndPrototypes(); + bool isEditableComponent = m_selectedNode.isComponent() + && !QmlItemNode(m_selectedNode).isEffectItem(); + auto specificQmlData = m_propertyEditorComponentGenerator.create(selfAndPrototypes, + isEditableComponent); + auto [panePath, specificsPath] = findPaneAndSpecificsPath(selfAndPrototypes, model()->pathCache()); + PropertyEditorQmlBackend *currentQmlBackend = getQmlBackend(m_qmlBackendHash, + QUrl::fromLocalFile(QString{panePath}), + m_imageCache, + m_stackedWidget, + this); - setupCurrentQmlBackend(currentQmlBackend, - m_selectedNode, - QUrl::fromLocalFile(QString{specificsPath}), - currentState(), - this, - specificQmlData); + setupCurrentQmlBackend(currentQmlBackend, + m_selectedNode, + QUrl::fromLocalFile(QString{specificsPath}), + currentState(), + this, + specificQmlData); - setupWidget(currentQmlBackend, this, m_stackedWidget); + setupWidget(currentQmlBackend, this, m_stackedWidget); - m_qmlBackEndForCurrentType = currentQmlBackend; + m_qmlBackEndForCurrentType = currentQmlBackend; - setupInsight(rootModelNode(), currentQmlBackend); - } else { - const NodeMetaInfo commonAncestor = PropertyEditorQmlBackend::findCommonAncestor( - m_selectedNode); + setupInsight(rootModelNode(), currentQmlBackend); +#else + const NodeMetaInfo commonAncestor = PropertyEditorQmlBackend::findCommonAncestor(m_selectedNode); - const auto [qmlFileUrl, specificsClassMetaInfo] = PropertyEditorQmlBackend::getQmlUrlForMetaInfo( - commonAncestor); + const auto [qmlFileUrl, specificsClassMetaInfo] = PropertyEditorQmlBackend::getQmlUrlForMetaInfo( + commonAncestor); - auto [diffClassMetaInfo, qmlSpecificsFile] = diffType(commonAncestor, specificsClassMetaInfo); + auto [diffClassMetaInfo, qmlSpecificsFile] = diffType(commonAncestor, specificsClassMetaInfo); - QString specificQmlData = getSpecificQmlData(commonAncestor, m_selectedNode, diffClassMetaInfo); + QString specificQmlData = getSpecificQmlData(commonAncestor, m_selectedNode, diffClassMetaInfo); - PropertyEditorQmlBackend *currentQmlBackend = getQmlBackend(m_qmlBackendHash, - qmlFileUrl, - m_imageCache, - m_stackedWidget, - this); + PropertyEditorQmlBackend *currentQmlBackend = getQmlBackend(m_qmlBackendHash, + qmlFileUrl, + m_imageCache, + m_stackedWidget, + this); - setupCurrentQmlBackend(currentQmlBackend, - m_selectedNode, - qmlSpecificsFile, - currentState(), - this, - specificQmlData); + setupCurrentQmlBackend( + currentQmlBackend, m_selectedNode, qmlSpecificsFile, currentState(), this, specificQmlData); - setupWidget(currentQmlBackend, this, m_stackedWidget); + setupWidget(currentQmlBackend, this, m_stackedWidget); - m_qmlBackEndForCurrentType = currentQmlBackend; + m_qmlBackEndForCurrentType = currentQmlBackend; - setupInsight(rootModelNode(), currentQmlBackend); - } + setupInsight(rootModelNode(), currentQmlBackend); +#endif // QDS_USE_PROJECTSTORAGE } void PropertyEditorView::commitVariantValueToModel(const PropertyName &propertyName, const QVariant &value) diff --git a/src/plugins/qmldesigner/components/stateseditor/propertymodel.cpp b/src/plugins/qmldesigner/components/stateseditor/propertymodel.cpp index 0a72d60ef7a..db07dd22080 100644 --- a/src/plugins/qmldesigner/components/stateseditor/propertymodel.cpp +++ b/src/plugins/qmldesigner/components/stateseditor/propertymodel.cpp @@ -64,12 +64,19 @@ QVariant PropertyModel::data(const QModelIndex &index, int role) const if (!propertyChanges.target().isValid()) return {}; - +#ifdef QDS_USE_PROJECTSTORAGE + return propertyChanges.target() + .metaInfo() + .property(m_properties.at(index.row()).name()) + .propertyType() + .displayName(); +#else return propertyChanges.target() .metaInfo() .property(m_properties.at(index.row()).name()) .propertyType() .typeName(); +#endif } } return {}; diff --git a/src/plugins/qmldesigner/components/stateseditor/stateseditormodel.cpp b/src/plugins/qmldesigner/components/stateseditor/stateseditormodel.cpp index 2ff5adedbe7..33351baf5ea 100644 --- a/src/plugins/qmldesigner/components/stateseditor/stateseditormodel.cpp +++ b/src/plugins/qmldesigner/components/stateseditor/stateseditormodel.cpp @@ -343,6 +343,9 @@ bool StatesEditorModel::renameActiveStateGroup(const QString &name) void StatesEditorModel::addStateGroup(const QString &name) { m_statesEditorView->executeInTransaction("createStateGroup", [this, name]() { +#ifdef QDS_USE_PROJECTSTORAGE + auto stateGroupNode = m_statesEditorView->createModelNode("StateGroup"); +#else const TypeName typeName = "QtQuick.StateGroup"; auto metaInfo = m_statesEditorView->model()->metaInfo(typeName); int minorVersion = metaInfo.minorVersion(); @@ -350,6 +353,7 @@ void StatesEditorModel::addStateGroup(const QString &name) auto stateGroupNode = m_statesEditorView->createModelNode(typeName, majorVersion, minorVersion); +#endif stateGroupNode.setIdWithoutRefactoring(m_statesEditorView->model()->generateNewId(name)); m_statesEditorView->rootModelNode().defaultNodeAbstractProperty().reparentHere( diff --git a/src/plugins/qmldesigner/components/textureeditor/textureeditorqmlbackend.cpp b/src/plugins/qmldesigner/components/textureeditor/textureeditorqmlbackend.cpp index 0cf01cf6209..80cf5693d28 100644 --- a/src/plugins/qmldesigner/components/textureeditor/textureeditorqmlbackend.cpp +++ b/src/plugins/qmldesigner/components/textureeditor/textureeditorqmlbackend.cpp @@ -247,8 +247,10 @@ void TextureEditorQmlBackend::setup(const QmlObjectNode &selectedTextureNode, co contextObject()->setSelectionChanged(false); +#ifndef QDS_USE_PROJECTSTORAGE NodeMetaInfo metaInfo = selectedTextureNode.modelNode().metaInfo(); contextObject()->setMajorVersion(metaInfo.isValid() ? metaInfo.majorVersion() : -1); +#endif } else { context()->setContextProperty("hasTexture", QVariant(false)); } diff --git a/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp b/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp index 5cdd788ab7b..5de3730c97b 100644 --- a/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp +++ b/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp @@ -380,10 +380,14 @@ void TextureEditorView::handleToolBarAction(int action) ModelNode matLib = Utils3D::materialLibraryNode(this); if (!matLib.isValid()) return; - +#ifdef QDS_USE_PROJECTSTORAGE + ModelNode newTextureNode = createModelNode("Texture"); +#else NodeMetaInfo metaInfo = model()->metaInfo("QtQuick3D.Texture"); - ModelNode newTextureNode = createModelNode("QtQuick3D.Texture", metaInfo.majorVersion(), - metaInfo.minorVersion()); + ModelNode newTextureNode = createModelNode("QtQuick3D.Texture", + metaInfo.majorVersion(), + metaInfo.minorVersion()); +#endif newTextureNode.validId(); matLib.defaultNodeListProperty().reparentHere(newTextureNode); }); @@ -408,6 +412,9 @@ void TextureEditorView::handleToolBarAction(int action) void TextureEditorView::setupQmlBackend() { +#ifdef QDS_USE_PROJECTSTORAGE +// This is an copy of the property editor code which is already rewritten. Please reuse that code. +#else QUrl qmlPaneUrl; QUrl qmlSpecificsUrl; QString specificQmlData; @@ -475,6 +482,7 @@ void TextureEditorView::setupQmlBackend() m_dynamicPropertiesModel->reset(); m_stackedWidget->setCurrentWidget(m_qmlBackEnd->widget()); +#endif } void TextureEditorView::commitVariantValueToModel(const PropertyName &propertyName, const QVariant &value) @@ -754,9 +762,12 @@ void TextureEditorView::duplicateTexture(const ModelNode &texture) return; // create the duplicate texture +#ifdef QDS_USE_PROJECTSTORAGE + QmlObjectNode duplicateTex = createModelNode(matType); +#else NodeMetaInfo metaInfo = model()->metaInfo(matType); QmlObjectNode duplicateTex = createModelNode(matType, metaInfo.majorVersion(), metaInfo.minorVersion()); - +#endif duplicateTextureNode = duplicateTex .modelNode(); duplicateTextureNode.validId(); diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelineview.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelineview.cpp index c363f944099..7905df68e91 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelineview.cpp +++ b/src/plugins/qmldesigner/components/timelineeditor/timelineview.cpp @@ -278,6 +278,7 @@ TimelineWidget *TimelineView::widget() const return m_timelineWidget; } +namespace { QList getAllStates(TimelineView* view) { QmlVisualNode visNode(view->rootModelNode()); @@ -315,6 +316,7 @@ void enableInCurrentState( } } } +} // namespace const QmlTimeline TimelineView::addNewTimeline() { @@ -329,21 +331,24 @@ const QmlTimeline TimelineView::addNewTimeline() } catch (const Exception &e) { e.showException(); } - +#ifndef QDS_USE_PROJECTSTORAGE NodeMetaInfo metaInfo = model()->metaInfo(timelineType); QTC_ASSERT(metaInfo.isValid(), return QmlTimeline()); - +#endif ModelNode timelineNode; - executeInTransaction("TimelineView::addNewTimeline", - [this, timelineType, metaInfo, &timelineNode] { + executeInTransaction("TimelineView::addNewTimeline", [&] { bool hasTimelines = getTimelines().isEmpty(); QString currentStateName = getStateName(this, hasTimelines); +#ifdef QDS_USE_PROJECTSTORAGE + timelineNode = createModelNode("Timeline"); +#else timelineNode = createModelNode(timelineType, metaInfo.majorVersion(), metaInfo.minorVersion()); +#endif timelineNode.validId(); timelineNode.variantProperty("startFrame").setValue(0); @@ -366,21 +371,25 @@ ModelNode TimelineView::addAnimation(QmlTimeline timeline) QTC_ASSERT(isAttached(), return ModelNode()); +#ifndef QDS_USE_PROJECTSTORAGE NodeMetaInfo metaInfo = model()->metaInfo(animationType); QTC_ASSERT(metaInfo.isValid(), return ModelNode()); - +#endif ModelNode animationNode; - executeInTransaction("TimelineView::addAnimation", - [this, timeline, animationType, metaInfo, &animationNode] { + executeInTransaction("TimelineView::addAnimation", [&] { bool hasAnimations = getAnimations(timeline).isEmpty(); QString currentStateName = getStateName(this, hasAnimations); +#ifdef QDS_USE_PROJECTSTORAGE + animationNode = createModelNode("TimelineAnimation"); +#else animationNode = createModelNode(animationType, metaInfo.majorVersion(), metaInfo.minorVersion()); animationNode.variantProperty("duration").setValue(timeline.duration()); +#endif animationNode.validId(); animationNode.variantProperty("from").setValue(timeline.startKeyframe()); diff --git a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.cpp b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.cpp index c24ebc1ce32..104127bd49d 100644 --- a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.cpp +++ b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.cpp @@ -225,8 +225,19 @@ ModelNode TransitionEditorView::addNewTransition() if (!idPropertyList.isEmpty()) { executeInTransaction( - " TransitionEditorView::addNewTransition", [&transition, idPropertyList, root, this]() { + " TransitionEditorView::addNewTransition", [&transition, idPropertyList, root, this]() { +#ifdef QDS_USE_PROJECTSTORAGE + transition = createModelNode("Transition", + {{ + "from", + "*", + }, + { + "to", + "*", + }}); +#else const NodeMetaInfo transitionMetaInfo = model()->metaInfo("QtQuick.Transition"); transition = createModelNode("QtQuick.Transition", transitionMetaInfo.majorVersion(), @@ -239,28 +250,38 @@ ModelNode TransitionEditorView::addNewTransition() "to", "*", }}); - transition.setAuxiliaryData(transitionDurationProperty, 2000); - transition.validId(); - root.nodeListProperty("transitions").reparentHere(transition); +#endif + transition.setAuxiliaryData(transitionDurationProperty, 2000); + transition.validId(); + root.nodeListProperty("transitions").reparentHere(transition); - for (auto it = idPropertyList.cbegin(); it != idPropertyList.cend(); ++it) { - ModelNode parallelAnimation = createModelNode("QtQuick.ParallelAnimation"); - transition.defaultNodeAbstractProperty().reparentHere(parallelAnimation); - for (const QString &property : it.value()) { - ModelNode sequentialAnimation - = createModelNode("QtQuick.SequentialAnimation"); - parallelAnimation.defaultNodeAbstractProperty().reparentHere( - sequentialAnimation); + for (auto it = idPropertyList.cbegin(); it != idPropertyList.cend(); ++it) { + ModelNode parallelAnimation = createModelNode("QtQuick.ParallelAnimation"); + transition.defaultNodeAbstractProperty().reparentHere(parallelAnimation); + for (const QString &property : it.value()) { + ModelNode sequentialAnimation = createModelNode( + "QtQuick.SequentialAnimation"); + parallelAnimation.defaultNodeAbstractProperty().reparentHere( + sequentialAnimation); +#ifdef QDS_USE_PROJECTSTORAGE + ModelNode pauseAnimation = createModelNode("PauseAnimation", + {{"duration", 50}}); +#else const NodeMetaInfo pauseMetaInfo = model()->metaInfo("QtQuick.PauseAnimation"); ModelNode pauseAnimation = createModelNode("QtQuick.PauseAnimation", pauseMetaInfo.majorVersion(), pauseMetaInfo.minorVersion(), {{"duration", 50}}); - sequentialAnimation.defaultNodeAbstractProperty().reparentHere( - pauseAnimation); +#endif + sequentialAnimation.defaultNodeAbstractProperty().reparentHere(pauseAnimation); +#ifdef QDS_USE_PROJECTSTORAGE + ModelNode propertyAnimation = createModelNode("PropertyAnimation", + {{"property", property}, + {"duration", 150}}); +#else const NodeMetaInfo propertyMetaInfo = model()->metaInfo("QtQuick.PauseAnimation"); ModelNode propertyAnimation = createModelNode("QtQuick.PropertyAnimation", @@ -268,11 +289,12 @@ ModelNode TransitionEditorView::addNewTransition() propertyMetaInfo.minorVersion(), {{"property", property}, {"duration", 150}}); - propertyAnimation.bindingProperty("target").setExpression(it.key()); - sequentialAnimation.defaultNodeAbstractProperty().reparentHere( - propertyAnimation); +#endif + propertyAnimation.bindingProperty("target").setExpression(it.key()); + sequentialAnimation.defaultNodeAbstractProperty().reparentHere( + propertyAnimation); + } } - } }); } else { QString properties; diff --git a/src/plugins/qmldesigner/designercore/include/abstractview.h b/src/plugins/qmldesigner/designercore/include/abstractview.h index 385739aa829..f4565633482 100644 --- a/src/plugins/qmldesigner/designercore/include/abstractview.h +++ b/src/plugins/qmldesigner/designercore/include/abstractview.h @@ -95,6 +95,13 @@ public: ModelNode::NodeSourceType nodeSourceType = ModelNode::NodeWithoutSource, const QString &behaviorPropertyName = {}); + ModelNode createModelNode(const TypeName &typeName, + const PropertyListType &propertyList, + const AuxiliaryDatas &auxPropertyList = {}, + const QString &nodeSource = {}, + ModelNode::NodeSourceType nodeSourceType = ModelNode::NodeWithoutSource, + const QString &behaviorPropertyName = {}); + ModelNode rootModelNode() const; ModelNode rootModelNode(); diff --git a/src/plugins/qmldesigner/designercore/include/model.h b/src/plugins/qmldesigner/designercore/include/model.h index 67179efede3..1f6f6dbcad8 100644 --- a/src/plugins/qmldesigner/designercore/include/model.h +++ b/src/plugins/qmldesigner/designercore/include/model.h @@ -110,13 +110,14 @@ public: fileUrl, std::move(resourceManagement))); } - static ModelPointer create(ProjectStorageDependencies m_projectStorageDependencies, + + static ModelPointer create(ProjectStorageDependencies projectStorageDependencies, const TypeName &typeName, int major = 1, int minor = 1, std::unique_ptr resourceManagement = {}) { - return ModelPointer(new Model(m_projectStorageDependencies, + return ModelPointer(new Model(projectStorageDependencies, typeName, major, minor, @@ -124,6 +125,9 @@ public: std::move(resourceManagement))); } + ModelPointer createModel(const TypeName &typeName, + std::unique_ptr resourceManagement = {}); + QUrl fileUrl() const; SourceId fileUrlSourceId() const; void setFileUrl(const QUrl &url); @@ -150,6 +154,7 @@ public: NodeMetaInfo flowViewFlowWildcardMetaInfo() const; NodeMetaInfo fontMetaInfo() const; NodeMetaInfo qmlQtObjectMetaInfo() const; + NodeMetaInfo qtQmlConnectionsMetaInfo() const; NodeMetaInfo qtQmlModelsListModelMetaInfo() const; NodeMetaInfo qtQmlModelsListElementMetaInfo() const; NodeMetaInfo qtQuick3DBakedLightmapMetaInfo() const; @@ -164,7 +169,6 @@ public: NodeMetaInfo qtQuick3DPrincipledMaterialMetaInfo() const; NodeMetaInfo qtQuick3DSpotLightMetaInfo() const; NodeMetaInfo qtQuick3DTextureMetaInfo() const; - NodeMetaInfo qtQuickConnectionsMetaInfo() const; NodeMetaInfo qtQuickControlsTextAreaMetaInfo() const; NodeMetaInfo qtQuickImageMetaInfo() const; NodeMetaInfo qtQuickItemMetaInfo() const; @@ -216,6 +220,10 @@ public: QStringList importPaths() const; Import highestPossibleImport(const QString &importPath); + ModuleIds moduleIds() const; + + Storage::Info::ExportedTypeName exportedTypeNameForMetaInfo(const NodeMetaInfo &metaInfo) const; + RewriterView *rewriterView() const; void setRewriterView(RewriterView *rewriterView); diff --git a/src/plugins/qmldesigner/designercore/include/nodemetainfo.h b/src/plugins/qmldesigner/designercore/include/nodemetainfo.h index 7e171c54450..53c755ddc88 100644 --- a/src/plugins/qmldesigner/designercore/include/nodemetainfo.h +++ b/src/plugins/qmldesigner/designercore/include/nodemetainfo.h @@ -22,6 +22,16 @@ QT_BEGIN_NAMESPACE class QDeclarativeContext; QT_END_NAMESPACE +#ifdef QDS_USE_PROJECTSTORAGE +# define DEPRECATED_TYPENAME [[deprecated("Don't use string based types anymore!")]] +# define DEPRECATED_VERSION_NUMBER \ + [[deprecated( \ + "In most cases you don't need them anymore because the import is setting them!")]] +#else +# define DEPRECATED_TYPENAME +# define DEPRECATED_VERSION_NUMBER +#endif + namespace QmlDesigner { class MetaInfo; @@ -92,10 +102,11 @@ public: bool defaultPropertyIsComponent() const; - TypeName typeName() const; - TypeName simplifiedTypeName() const; - int majorVersion() const; - int minorVersion() const; + TypeName displayName() const; + DEPRECATED_TYPENAME TypeName typeName() const; + DEPRECATED_TYPENAME TypeName simplifiedTypeName() const; + DEPRECATED_VERSION_NUMBER int majorVersion() const; + DEPRECATED_VERSION_NUMBER int minorVersion() const; Storage::Info::ExportedTypeNames allExportedTypeNames() const; Storage::Info::ExportedTypeNames exportedTypeNamesForSourceId(SourceId sourceId) const; @@ -155,6 +166,7 @@ public: bool isQmlComponent() const; bool isQtMultimediaSoundEffect() const; bool isQtObject() const; + bool isQtQmlConnections() const; bool isQtQuick3DBakedLightmap() const; bool isQtQuick3DBuffer() const; bool isQtQuick3DCamera() const; @@ -202,6 +214,7 @@ public: bool isQtQuickState() const; bool isQtQuickStateOperation() const; bool isQtQuickStudioComponentsGroupItem() const; + bool isQtQuickStudioUtilsJsonListModel() const; bool isQtQuickText() const; bool isQtQuickTimelineKeyframe() const; bool isQtQuickTimelineKeyframeGroup() const; diff --git a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp index bc405057340..3002f4d8de8 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp @@ -26,6 +26,12 @@ #include #include +// remove that if the old code model is removed +QT_WARNING_PUSH +QT_WARNING_DISABLE_GCC("-Wdeprecated-declarations") +QT_WARNING_DISABLE_CLANG("-Wdeprecated-declarations") +QT_WARNING_DISABLE_MSVC(4996) + namespace QmlDesigner { /*! @@ -1924,6 +1930,11 @@ bool NodeMetaInfo::defaultPropertyIsComponent() const return false; } +TypeName NodeMetaInfo::displayName() const +{ + return {}; +} + TypeName NodeMetaInfo::typeName() const { if (isValid()) @@ -2365,6 +2376,16 @@ bool NodeMetaInfo::isQtObject() const } } +bool NodeMetaInfo::isQtQmlConnections() const +{ + if constexpr (useProjectStorage()) { + using namespace Storage::Info; + return isBasedOnCommonType(m_projectStorage, m_typeId); + } else { + return isValid() && simplifiedTypeName() == "Connections"; + } +} + bool NodeMetaInfo::isLayoutable() const { if constexpr (useProjectStorage()) { @@ -3014,6 +3035,17 @@ bool NodeMetaInfo::isQtQuickStudioComponentsGroupItem() const } } +bool NodeMetaInfo::isQtQuickStudioUtilsJsonListModel() const +{ + if constexpr (useProjectStorage()) { + using namespace Storage::Info; + return isBasedOnCommonType(m_projectStorage, + m_typeId); + } else { + return isValid() && isSubclassOf("QtQuick.Studio.Utils.JsonListModel"); + } +} + bool NodeMetaInfo::isQmlComponent() const { if constexpr (useProjectStorage()) { @@ -3689,3 +3721,5 @@ CompoundPropertyMetaInfos MetaInfoUtils::inflateValueAndReadOnlyProperties(Prope } } // namespace QmlDesigner + +QT_WARNING_POP diff --git a/src/plugins/qmldesigner/designercore/model/abstractview.cpp b/src/plugins/qmldesigner/designercore/model/abstractview.cpp index aa9422d5c49..061ab8ae2b0 100644 --- a/src/plugins/qmldesigner/designercore/model/abstractview.cpp +++ b/src/plugins/qmldesigner/designercore/model/abstractview.cpp @@ -68,12 +68,12 @@ RewriterTransaction AbstractView::beginRewriterTransaction(const QByteArray &ide ModelNode AbstractView::createModelNode(const TypeName &typeName) { - if constexpr (useProjectStorage()) { - return createModelNode(typeName, -1, -1); - } else { - const NodeMetaInfo metaInfo = model()->metaInfo(typeName); - return createModelNode(typeName, metaInfo.majorVersion(), metaInfo.minorVersion()); - } +#ifdef QDS_USE_PROJECTSTORAGE + return createModelNode(typeName, -1, -1); +#else + const NodeMetaInfo metaInfo = model()->metaInfo(typeName); + return createModelNode(typeName, metaInfo.majorVersion(), metaInfo.minorVersion()); +#endif } ModelNode AbstractView::createModelNode(const TypeName &typeName, @@ -90,6 +90,24 @@ ModelNode AbstractView::createModelNode(const TypeName &typeName, behaviorPropertyName), model(), this); } +ModelNode AbstractView::createModelNode(const TypeName &typeName, + const QList> &propertyList, + const AuxiliaryDatas &auxPropertyList, + const QString &nodeSource, + ModelNode::NodeSourceType nodeSourceType, + const QString &behaviorPropertyName) +{ + return ModelNode(model()->d->createNode(typeName, + -1, + -1, + propertyList, + auxPropertyList, + nodeSource, + nodeSourceType, + behaviorPropertyName), + model(), + this); +} // Returns the constant root model node. ModelNode AbstractView::rootModelNode() const @@ -824,6 +842,7 @@ static int getMajorVersionFromImport(const Model *model) return -1; } +#ifndef QDS_USE_PROJECTSTORAGE static int getMajorVersionFromNode(const ModelNode &modelNode) { if (modelNode.metaInfo().isValid()) { @@ -848,6 +867,7 @@ static int getMinorVersionFromNode(const ModelNode &modelNode) return 1; // default } +#endif int AbstractView::majorQtQuickVersion() const { @@ -855,7 +875,11 @@ int AbstractView::majorQtQuickVersion() const if (majorVersionFromImport >= 0) return majorVersionFromImport; +#ifdef QDS_USE_PROJECTSTORAGE + return -1; +#else return getMajorVersionFromNode(rootModelNode()); +#endif } int AbstractView::minorQtQuickVersion() const @@ -864,7 +888,11 @@ int AbstractView::minorQtQuickVersion() const if (minorVersionFromImport >= 0) return minorVersionFromImport; +#ifdef QDS_USE_PROJECTSTORAGE + return -1; +#else return getMinorVersionFromNode(rootModelNode()); +#endif } } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/model/model.cpp b/src/plugins/qmldesigner/designercore/model/model.cpp index 9f38819dee8..a4cd31b2a89 100644 --- a/src/plugins/qmldesigner/designercore/model/model.cpp +++ b/src/plugins/qmldesigner/designercore/model/model.cpp @@ -1731,6 +1731,16 @@ Model::Model(const TypeName &typeName, this, typeName, major, minor, metaInfoProxyModel, std::move(resourceManagement))) {} +ModelPointer Model::createModel(const TypeName &typeName, + std::unique_ptr resourceManagement) +{ + return Model::create({*d->projectStorage, *d->pathCache}, + typeName, + imports(), + fileUrl(), + std::move(resourceManagement)); +} + Model::~Model() = default; const Imports &Model::imports() const @@ -1738,6 +1748,16 @@ const Imports &Model::imports() const return d->imports(); } +ModuleIds Model::moduleIds() const +{ + return {}; +} + +Storage::Info::ExportedTypeName Model::exportedTypeNameForMetaInfo(const NodeMetaInfo &) const +{ + return {}; +} + const Imports &Model::possibleImports() const { return d->m_possibleImportList; @@ -1938,7 +1958,6 @@ void Model::setCurrentStateNode(const ModelNode &node) d->notifyCurrentStateChanged(node); } -// QTC_TEMP ModelNode Model::currentStateNode(AbstractView *view) { return ModelNode(d->currentStateNode(), this, view); @@ -2455,13 +2474,13 @@ NodeMetaInfo Model::qtQuickTransistionMetaInfo() const } } -NodeMetaInfo Model::qtQuickConnectionsMetaInfo() const +NodeMetaInfo Model::qtQmlConnectionsMetaInfo() const { if constexpr (useProjectStorage()) { using namespace Storage::Info; - return createNodeMetaInfo(); + return createNodeMetaInfo(); } else { - return metaInfo("QtQuick.Connections"); + return metaInfo("QtQml.Connections"); } } @@ -2714,12 +2733,12 @@ ModelNode createNode(Model *model, ModelNode Model::createModelNode(const TypeName &typeName) { - if constexpr (useProjectStorage()) { - return createNode(this, d.get(), typeName, -1, -1); - } else { - const NodeMetaInfo m = metaInfo(typeName); - return createNode(this, d.get(), typeName, m.majorVersion(), m.minorVersion()); - } +#ifdef QDS_USE_PROJECTSTORAGE + return createNode(this, d.get(), typeName, -1, -1); +#else + const NodeMetaInfo m = metaInfo(typeName); + return createNode(this, d.get(), typeName, m.majorVersion(), m.minorVersion()); +#endif } void Model::changeRootNodeType(const TypeName &type) diff --git a/src/plugins/qmldesigner/designercore/model/modelmerger.cpp b/src/plugins/qmldesigner/designercore/model/modelmerger.cpp index a9fa1ebd19b..24873bb141f 100644 --- a/src/plugins/qmldesigner/designercore/model/modelmerger.cpp +++ b/src/plugins/qmldesigner/designercore/model/modelmerger.cpp @@ -152,9 +152,17 @@ static ModelNode createNodeFromNode(const ModelNode &modelNode, const QHash &idRenamingHash, AbstractView *view, const MergePredicate &mergePredicate) { +#ifdef QDS_USE_PROJECTSTORAGE + ModelNode newNode = view->createModelNode(modelNode.type(), + {}, + {}, + modelNode.nodeSource(), + modelNode.nodeSourceType()); +#else NodeMetaInfo nodeMetaInfo = view->model()->metaInfo(modelNode.type()); ModelNode newNode(view->createModelNode(modelNode.type(), nodeMetaInfo.majorVersion(), nodeMetaInfo.minorVersion(), {}, {}, modelNode.nodeSource(), modelNode.nodeSourceType())); +#endif syncVariantProperties(newNode, modelNode); syncAuxiliaryProperties(newNode, modelNode); syncBindingProperties(newNode, modelNode, idRenamingHash); diff --git a/src/plugins/qmldesigner/designercore/model/qmlconnections.cpp b/src/plugins/qmldesigner/designercore/model/qmlconnections.cpp index 7a63e73d629..b7d04f3f387 100644 --- a/src/plugins/qmldesigner/designercore/model/qmlconnections.cpp +++ b/src/plugins/qmldesigner/designercore/model/qmlconnections.cpp @@ -98,10 +98,15 @@ QList QmlConnections::signalProperties() const ModelNode QmlConnections::createQmlConnections(AbstractView *view) { - NodeMetaInfo nodeMetaInfo = view->model()->qtQuickConnectionsMetaInfo(); +#ifdef QDS_USE_PROJECTSTORAGE + return view->createModelNode("Connections"); +#else + NodeMetaInfo nodeMetaInfo = view->model()->qtQmlConnectionsMetaInfo(); + return view->createModelNode("QtQuick.Connections", nodeMetaInfo.majorVersion(), nodeMetaInfo.minorVersion()); +#endif } } // QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp b/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp index 59295e6ae76..826856428bf 100644 --- a/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp +++ b/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp @@ -81,12 +81,25 @@ QmlItemNode QmlItemNode::createQmlItemNodeFromImage(AbstractView *view, const QS propertyPairList.append({PropertyName("source"), QVariant(relativeImageName)}); } +#ifdef QDS_USE_PROJECTSTORAGE + TypeName type("Image"); + QImageReader reader(imageName); + if (reader.supportsAnimation()) + type = "AnimatedImage"; + + newQmlItemNode = QmlItemNode(view->createModelNode(type, propertyPairList)); +#else + TypeName type("QtQuick.Image"); QImageReader reader(imageName); if (reader.supportsAnimation()) type = "QtQuick.AnimatedImage"; - newQmlItemNode = QmlItemNode(view->createModelNode(type, metaInfo.majorVersion(), metaInfo.minorVersion(), propertyPairList)); + newQmlItemNode = QmlItemNode(view->createModelNode(type, + metaInfo.majorVersion(), + metaInfo.minorVersion(), + propertyPairList)); +#endif parentproperty.reparentHere(newQmlItemNode); QFileInfo fi(relativeImageName); @@ -129,7 +142,6 @@ QmlItemNode QmlItemNode::createQmlItemNodeFromFont(AbstractView *view, QmlItemNode newQmlItemNode; auto doCreateQmlItemNodeFromFont = [=, &newQmlItemNode, &parentproperty]() { - NodeMetaInfo metaInfo = view->model()->metaInfo("QtQuick.Text"); QList> propertyPairList; if (const int intX = qRound(position.x())) propertyPairList.append({PropertyName("x"), QVariant(intX)}); @@ -138,9 +150,13 @@ QmlItemNode QmlItemNode::createQmlItemNodeFromFont(AbstractView *view, propertyPairList.append({PropertyName("font.family"), QVariant(fontFamily)}); propertyPairList.append({PropertyName("font.pointSize"), 20}); propertyPairList.append({PropertyName("text"), QVariant(fontFamily)}); - +#ifdef QDS_USE_PROJECTSTORAGE + newQmlItemNode = QmlItemNode(view->createModelNode("Text", propertyPairList)); +#else + NodeMetaInfo metaInfo = view->model()->metaInfo("QtQuick.Text"); newQmlItemNode = QmlItemNode(view->createModelNode("QtQuick.Text", metaInfo.majorVersion(), metaInfo.minorVersion(), propertyPairList)); +#endif parentproperty.reparentHere(newQmlItemNode); newQmlItemNode.setId(view->model()->generateNewId("text", "text")); diff --git a/src/plugins/qmldesigner/designercore/model/qmlobjectnode.cpp b/src/plugins/qmldesigner/designercore/model/qmlobjectnode.cpp index 55e56512e63..8e1d130295d 100644 --- a/src/plugins/qmldesigner/designercore/model/qmlobjectnode.cpp +++ b/src/plugins/qmldesigner/designercore/model/qmlobjectnode.cpp @@ -475,7 +475,7 @@ QList QmlObjectNode::getAllConnections() const if (!isValid()) return {}; - auto list = view()->allModelNodesOfType(model()->qtQuickConnectionsMetaInfo()); + auto list = view()->allModelNodesOfType(model()->qtQmlConnectionsMetaInfo()); return Utils::filtered(list, [this](const ModelNode &connection) { return connection.hasBindingProperty("target") && connection.bindingProperty("target").resolveToModelNode() == modelNode(); diff --git a/src/plugins/qmldesigner/designercore/model/qmlstate.cpp b/src/plugins/qmldesigner/designercore/model/qmlstate.cpp index 55864745a96..60ac06f2881 100644 --- a/src/plugins/qmldesigner/designercore/model/qmlstate.cpp +++ b/src/plugins/qmldesigner/designercore/model/qmlstate.cpp @@ -141,6 +141,9 @@ void QmlModelState::addChangeSetIfNotExists(const ModelNode &node) if (!hasPropertyChanges(node)) { ModelNode newChangeSet; +#ifdef QDS_USE_PROJECTSTORAGE + newChangeSet = modelNode().view()->createModelNode("PropertyChanges"); +#else const QByteArray typeName = "QtQuick.PropertyChanges"; NodeMetaInfo metaInfo = modelNode().model()->metaInfo(typeName); @@ -148,6 +151,7 @@ void QmlModelState::addChangeSetIfNotExists(const ModelNode &node) int minor = metaInfo.minorVersion(); newChangeSet = modelNode().view()->createModelNode(typeName, major, minor); +#endif modelNode().nodeListProperty("changes").reparentHere(newChangeSet); @@ -296,6 +300,9 @@ ModelNode QmlModelState::createQmlState(AbstractView *view, const PropertyListTy { QTC_ASSERT(view, return {}); +#ifdef QDS_USE_PROJECTSTORAGE + return view->createModelNode("State", propertyList); +#else const QByteArray typeName = "QtQuick.State"; NodeMetaInfo metaInfo = view->model()->metaInfo(typeName); @@ -303,6 +310,7 @@ ModelNode QmlModelState::createQmlState(AbstractView *view, const PropertyListTy int minor = metaInfo.minorVersion(); return view->createModelNode(typeName, major, minor, propertyList); +#endif } void QmlModelState::setAsDefault() diff --git a/src/plugins/qmldesigner/designercore/model/qmlvisualnode.cpp b/src/plugins/qmldesigner/designercore/model/qmlvisualnode.cpp index 75db18b5a22..c84f2342576 100644 --- a/src/plugins/qmldesigner/designercore/model/qmlvisualnode.cpp +++ b/src/plugins/qmldesigner/designercore/model/qmlvisualnode.cpp @@ -278,7 +278,11 @@ static QmlObjectNode createQmlObjectNodeFromSource(AbstractView *view, const QString &source, const QmlVisualNode::Position &position) { +#ifdef QDS_USE_PROJECTSTORAGE + auto inputModel = view->model()->createModel("Item"); +#else auto inputModel = Model::create("QtQuick.Item", 1, 0, view->model()); +#endif inputModel->setFileUrl(view->model()->fileUrl()); QPlainTextEdit textEdit; @@ -328,11 +332,12 @@ QmlObjectNode QmlVisualNode::createQmlObjectNode(AbstractView *view, NodeHints hints = NodeHints::fromItemLibraryEntry(itemLibraryEntry); auto createNodeFunc = [=, &newQmlObjectNode, &parentProperty]() { +#ifndef QDS_USE_PROJECTSTORAGE NodeMetaInfo metaInfo = view->model()->metaInfo(itemLibraryEntry.typeName()); int minorVersion = metaInfo.minorVersion(); int majorVersion = metaInfo.majorVersion(); - +#endif using PropertyBindingEntry = QPair; QList propertyBindingList; QList propertyEnumList; @@ -359,7 +364,18 @@ QmlObjectNode QmlVisualNode::createQmlObjectNode(AbstractView *view, if (itemLibraryEntry.typeName() == "QtQml.Component") nodeSourceType = ModelNode::NodeWithComponentSource; - newQmlObjectNode = QmlObjectNode(view->createModelNode(itemLibraryEntry.typeName(), majorVersion, minorVersion, propertyPairList, {}, {}, nodeSourceType)); +#ifdef QDS_USE_PROJECTSTORAGE + newQmlObjectNode = QmlObjectNode(view->createModelNode( + itemLibraryEntry.typeName(), propertyPairList, {}, {}, nodeSourceType)); +#else + newQmlObjectNode = QmlObjectNode(view->createModelNode(itemLibraryEntry.typeName(), + majorVersion, + minorVersion, + propertyPairList, + {}, + {}, + nodeSourceType)); +#endif } else { newQmlObjectNode = createQmlObjectNodeFromSource(view, itemLibraryEntry.qmlSource(), position); } diff --git a/src/plugins/qmldesigner/designercore/model/stylesheetmerger.cpp b/src/plugins/qmldesigner/designercore/model/stylesheetmerger.cpp index c4b4eb8c09c..8e0416b68aa 100644 --- a/src/plugins/qmldesigner/designercore/model/stylesheetmerger.cpp +++ b/src/plugins/qmldesigner/designercore/model/stylesheetmerger.cpp @@ -169,6 +169,11 @@ ModelNode StylesheetMerger::createReplacementNode(const ModelNode& styleNode, Mo continue; propertyList.append(QPair(variantProperty.name(), variantProperty.value())); } + +#ifdef QDS_USE_PROJECTSTORAGE + ModelNode newNode(m_templateView->createModelNode( + styleNode.type(), propertyList, {}, styleNode.nodeSource(), styleNode.nodeSourceType())); +#else ModelNode newNode(m_templateView->createModelNode(styleNode.type(), nodeMetaInfo.majorVersion(), nodeMetaInfo.minorVersion(), @@ -176,6 +181,7 @@ ModelNode StylesheetMerger::createReplacementNode(const ModelNode& styleNode, Mo {}, styleNode.nodeSource(), styleNode.nodeSourceType())); +#endif syncAuxiliaryProperties(newNode, modelNode); syncBindingProperties(newNode, modelNode); @@ -436,10 +442,14 @@ void StylesheetMerger::syncStateNode(ModelNode &outputState, const ModelNode &in changeSet = itr->second; } else { const QByteArray typeName = inputChangeset.type(); +#ifdef QDS_USE_PROJECTSTORAGE + changeSet = m_templateView->createModelNode(typeName); +#else NodeMetaInfo metaInfo = m_templateView->model()->metaInfo(typeName); int major = metaInfo.majorVersion(); int minor = metaInfo.minorVersion(); changeSet = m_templateView->createModelNode(typeName, major, minor); +#endif outputState.nodeListProperty("changes").reparentHere(changeSet); outputChangeSets.insert({key, changeSet}); } diff --git a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp index 942289f865e..f7be6cf5e46 100644 --- a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp +++ b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp @@ -1192,11 +1192,11 @@ void TextToModelMerger::syncNode(ModelNode &modelNode, int majorVersion = -1; int minorVersion = -1; - if constexpr (!useProjectStorage()) { - typeName = info.typeName(); - majorVersion = info.majorVersion(); - minorVersion = info.minorVersion(); - } +#ifndef QDS_USE_PROJECTSTORAGE + typeName = info.typeName(); + majorVersion = info.majorVersion(); + minorVersion = info.minorVersion(); +#endif if (modelNode.isRootNode() && !m_rewriterView->allowComponentRoot() && info.isQmlComponent()) { for (AST::UiObjectMemberList *iter = astInitializer->members; iter; iter = iter->next) { @@ -1563,11 +1563,12 @@ void TextToModelMerger::syncNodeProperty(AbstractProperty &modelProperty, int majorVersion = -1; int minorVersion = -1; - if constexpr (!useProjectStorage()) { - typeName = info.typeName(); - majorVersion = info.majorVersion(); - minorVersion = info.minorVersion(); - } + +#ifndef QDS_USE_PROJECTSTORAGE + typeName = info.typeName(); + majorVersion = info.majorVersion(); + minorVersion = info.minorVersion(); +#endif if (modelProperty.isNodeProperty() && dynamicPropertyType == modelProperty.dynamicTypeName()) { ModelNode nodePropertyNode = modelProperty.toNodeProperty().modelNode(); @@ -2104,11 +2105,11 @@ ModelNode ModelAmender::listPropertyMissingModelNode(NodeListProperty &modelProp int majorVersion = -1; int minorVersion = -1; - if constexpr (!useProjectStorage()) { - typeName = info.typeName(); - majorVersion = info.majorVersion(); - minorVersion = info.minorVersion(); - } +#ifndef QDS_USE_PROJECTSTORAGE + typeName = info.typeName(); + majorVersion = info.majorVersion(); + minorVersion = info.minorVersion(); +#endif const bool propertyTakesComponent = propertyHasImplicitComponentType(modelProperty, info); diff --git a/src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h b/src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h index 41f894356ab..03c25dfac79 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h @@ -55,6 +55,7 @@ inline constexpr char InstanceListEntry[] = "InstanceListEntry"; inline constexpr char InstanceList[] = "InstanceList"; inline constexpr char IntType[] = "int"; inline constexpr char Item[] = "Item"; +inline constexpr char JsonListModel[] = "JsonListModel"; inline constexpr char KeyframeGroup[] = "KeyframeGroup"; inline constexpr char Keyframe[] = "Keyframe"; inline constexpr char Layout[] = "Layout"; @@ -171,11 +172,11 @@ class CommonTypeCache CacheType, CacheType, CacheType, + CacheType, CacheType, CacheType, CacheType, CacheType, - CacheType, CacheType, CacheType, CacheType, @@ -243,6 +244,7 @@ class CommonTypeCache CacheType, CacheType, CacheType, + CacheType, CacheType, CacheType, CacheType, diff --git a/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp b/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp index 2f229985960..7e28849fbb3 100644 --- a/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp +++ b/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp @@ -281,12 +281,12 @@ AsynchronousImageCache &QmlDesignerProjectManager::asynchronousImageCache() } namespace { -ProjectStorage *dummyProjectStorage() +[[maybe_unused]] ProjectStorage *dummyProjectStorage() { return nullptr; } -ProjectStorageUpdater::PathCache *dummyPathCache() +[[maybe_unused]] ProjectStorageUpdater::PathCache *dummyPathCache() { return nullptr; } @@ -383,7 +383,7 @@ void collectQmldirPaths(const QString &path, QStringList &qmldirPaths) } } -void projectQmldirPaths(::ProjectExplorer::Target *target, QStringList &qmldirPaths) +[[maybe_unused]] void projectQmldirPaths(::ProjectExplorer::Target *target, QStringList &qmldirPaths) { ::QmlProjectManager::QmlBuildSystem *buildSystem = getQmlBuildSystem(target); @@ -395,12 +395,22 @@ void projectQmldirPaths(::ProjectExplorer::Target *target, QStringList &qmldirPa collectQmldirPaths(importPath, qmldirPaths); } -void qtQmldirPaths(::ProjectExplorer::Target *target, QStringList &qmldirPaths) +[[maybe_unused]] void qtQmldirPaths(::ProjectExplorer::Target *target, QStringList &qmldirPaths) { if constexpr (useProjectStorage()) collectQmldirPaths(qmlPath(target).toString(), qmldirPaths); } +[[maybe_unused]] void qtQmldirPathsForLiteDesigner(::ProjectExplorer::Target *target, + QStringList &qmldirPaths) +{ + if constexpr (useProjectStorage()) { + auto qmlRootPath = qmlPath(target).toString(); + collectQmldirPaths(qmlRootPath + "/QtQml", qmldirPaths); + collectQmldirPaths(qmlRootPath + "/QtQuick", qmldirPaths); + } +} + QStringList directories(::ProjectExplorer::Target *target) { if (!target) @@ -409,8 +419,12 @@ QStringList directories(::ProjectExplorer::Target *target) QStringList qmldirPaths; qmldirPaths.reserve(100); - qtQmldirPaths(target, qmldirPaths); - projectQmldirPaths(target, qmldirPaths); + if constexpr (isUsingQmlDesignerLite()) { + qtQmldirPathsForLiteDesigner(target, qmldirPaths); + } else { + qtQmldirPaths(target, qmldirPaths); + projectQmldirPaths(target, qmldirPaths); + } std::sort(qmldirPaths.begin(), qmldirPaths.end()); qmldirPaths.erase(std::unique(qmldirPaths.begin(), qmldirPaths.end()), qmldirPaths.end()); diff --git a/tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp b/tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp index 9be03d22612..ca82e95b8c1 100644 --- a/tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp +++ b/tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp @@ -1365,6 +1365,14 @@ void tst_TestCore::testRewriterBehaivours() QVERIFY(metaInfo.isValid()); +#ifdef QDS_USE_PROJECTSTORAGE + ModelNode newBehavior = testRewriterView->createModelNode("Behavior", + {}, + {}, + {}, + ModelNode::NodeWithoutSource, + "height"); +#else ModelNode newBehavior = testRewriterView->createModelNode("QtQuick.Behavior", metaInfo.majorVersion(), metaInfo.minorVersion(), @@ -1373,17 +1381,20 @@ void tst_TestCore::testRewriterBehaivours() {}, ModelNode::NodeWithoutSource, "height"); - +#endif rootModelNode.defaultNodeListProperty().reparentHere(newBehavior); QCOMPARE(newBehavior.behaviorPropertyName(), "height"); metaInfo = animation.metaInfo(); QVERIFY(metaInfo.isValid()); +#ifdef QDS_USE_PROJECTSTORAGE + ModelNode newAnimation = testRewriterView->createModelNode(model->exportedTypeNameForMetaInfo(metaInfo).name.toQByteArray()); +#else ModelNode newAnimation = testRewriterView->createModelNode(metaInfo.typeName(), metaInfo.majorVersion(), metaInfo.minorVersion()); - +#endif newBehavior.defaultNodeListProperty().reparentHere(newAnimation); newAnimation.variantProperty("duration").setValue(500); @@ -1666,8 +1677,10 @@ void tst_TestCore::testStatesVersionFailing() QCOMPARE(QmlItemNode(rootModelNode).states().state("state2"), newState); +#ifndef QDS_USE_PROJECTSTORAGE QCOMPARE(stateInfo.majorVersion(), newState.modelNode().majorVersion()); QCOMPARE(stateInfo.minorVersion(), newState.modelNode().minorVersion()); +#endif ModelNode rect1Node = view->modelNodeForId("rect1"); QVERIFY(rect1Node.isValid()); @@ -1708,8 +1721,10 @@ void tst_TestCore::testStatesVersionFailing() QVERIFY(changes2.modelNode().hasProperty("x")); QVERIFY(oldText != textEdit.toPlainText()); +#ifndef QDS_USE_PROJECTSTORAGE QCOMPARE(changeInfo.majorVersion(), changes2.modelNode().majorVersion()); QCOMPARE(changeInfo.minorVersion(), changes2.modelNode().minorVersion()); +#endif } void tst_TestCore::loadSubItems() @@ -1987,8 +2002,10 @@ void tst_TestCore::testBasicStatesQtQuick20() QCOMPARE(rootModelNode.majorVersion(), 2); //QCOMPARE(rootModelNode.majorQtQuickVersion(), 2); +#ifndef QDS_USE_PROJECTSTORAGE qDebug() << rootModelNode.nodeListProperty("states").toModelNodeList().first().metaInfo().majorVersion(); qDebug() << rootModelNode.nodeListProperty("states").toModelNodeList().first().metaInfo().typeName(); +#endif QSKIP("No qml2puppet"); @@ -4833,9 +4850,11 @@ void tst_TestCore::testMetaInfoSimpleType() NodeMetaInfo itemMetaInfo = model->metaInfo("QtQuick.Item", 2, 1); QVERIFY(itemMetaInfo.isValid()); +#ifndef QDS_USE_PROJECTSTORAGE QCOMPARE(itemMetaInfo.typeName(), QmlDesigner::TypeName("QtQuick.Item")); QCOMPARE(itemMetaInfo.majorVersion(), 2); QCOMPARE(itemMetaInfo.minorVersion(), 1); +#endif // super classes NodeMetaInfo qobject = itemMetaInfo.prototypes()[1]; @@ -4857,13 +4876,17 @@ void tst_TestCore::testMetaInfoUncreatableType() QVERIFY(animationTypeInfo.isValid()); QVERIFY(animationTypeInfo.isValid()); +#ifndef QDS_USE_PROJECTSTORAGE QCOMPARE(animationTypeInfo.typeName(), QmlDesigner::TypeName("QtQuick.Animation")); QCOMPARE(animationTypeInfo.majorVersion(), 2); QCOMPARE(animationTypeInfo.minorVersion(), 1); +#endif NodeMetaInfo qObjectTypeInfo = animationTypeInfo.prototypes()[1]; QVERIFY(qObjectTypeInfo.isValid()); +#ifndef QDS_USE_PROJECTSTORAGE QCOMPARE(qObjectTypeInfo.simplifiedTypeName(), QmlDesigner::TypeName("QtObject")); +#endif QCOMPARE(animationTypeInfo.prototypes().size(), 2); } @@ -4903,9 +4926,11 @@ void tst_TestCore::testMetaInfoCustomType() NodeMetaInfo stateOperationInfo = propertyChangesInfo.prototypes()[1]; QVERIFY(stateOperationInfo.isValid()); +#ifndef QDS_USE_PROJECTSTORAGE QCOMPARE(stateOperationInfo.typeName(), QmlDesigner::TypeName("QtQuick.QQuickStateOperation")); QCOMPARE(stateOperationInfo.majorVersion(), -1); QCOMPARE(stateOperationInfo.minorVersion(), -1); +#endif QCOMPARE(propertyChangesInfo.prototypes().size(), 3); // DeclarativePropertyChanges just has 3 properties @@ -4923,25 +4948,31 @@ void tst_TestCore::testMetaInfoEnums() QVERIFY(view.data()); model->attachView(view.data()); +#ifndef QDS_USE_PROJECTSTORAGE QCOMPARE(view->rootModelNode().metaInfo().typeName(), QmlDesigner::TypeName("QtQuick.Text")); +#endif QVERIFY(view->rootModelNode().metaInfo().hasProperty("transformOrigin")); QVERIFY(view->rootModelNode().metaInfo().property("transformOrigin").isEnumType()); +#ifndef QDS_USE_PROJECTSTORAGE QCOMPARE(view->rootModelNode() .metaInfo() .property("transformOrigin") .propertyType() .simplifiedTypeName(), QmlDesigner::TypeName("TransformOrigin")); +#endif QVERIFY(view->rootModelNode().metaInfo().property("horizontalAlignment").isEnumType()); +#ifndef QDS_USE_PROJECTSTORAGE QCOMPARE(view->rootModelNode() .metaInfo() .property("horizontalAlignment") .propertyType() .simplifiedTypeName(), QmlDesigner::TypeName("HAlignment")); +#endif QApplication::processEvents(); } @@ -5038,10 +5069,12 @@ void tst_TestCore::testMetaInfoDotProperties() QVERIFY(model->hasNodeMetaInfo("QtQuick.Text")); QVERIFY(model->metaInfo("QtQuick.Rectangle").hasProperty("border")); +#ifndef QDS_USE_PROJECTSTORAGE QCOMPARE(model->metaInfo("QtQuick.Rectangle").property("border").propertyType().typeName(), QmlDesigner::TypeName(".QQuickPen")); QCOMPARE(view->rootModelNode().metaInfo().typeName(), QmlDesigner::TypeName("QtQuick.Text")); +#endif QVERIFY(view->rootModelNode().metaInfo().hasProperty("font")); QVERIFY(view->rootModelNode().metaInfo().hasProperty("font.bold")); @@ -5071,7 +5104,9 @@ void tst_TestCore::testMetaInfoListProperties() model->attachView(view.data()); QVERIFY(model->hasNodeMetaInfo("QtQuick.Item")); +#ifndef QDS_USE_PROJECTSTORAGE QCOMPARE(view->rootModelNode().metaInfo().typeName(), QmlDesigner::TypeName("QtQuick.Item")); +#endif QVERIFY(view->rootModelNode().metaInfo().hasProperty("states")); QVERIFY(view->rootModelNode().metaInfo().property("states").isListProperty()); @@ -5108,10 +5143,12 @@ void tst_TestCore::testQtQuick20Basic() QVERIFY(testRewriterView->errors().isEmpty()); ModelNode rootModelNode(testRewriterView->rootModelNode()); QVERIFY(rootModelNode.isValid()); +#ifndef QDS_USE_PROJECTSTORAGE QCOMPARE(rootModelNode.metaInfo().majorVersion(), 2); QCOMPARE(rootModelNode.metaInfo().minorVersion(), 0); //QCOMPARE(rootModelNode.majorQtQuickVersion(), 2); QCOMPARE(rootModelNode.majorVersion(), 2); +#endif } void tst_TestCore::testQtQuick20BasicRectangle() @@ -5133,11 +5170,13 @@ void tst_TestCore::testQtQuick20BasicRectangle() QVERIFY(testRewriterView->errors().isEmpty()); ModelNode rootModelNode(testRewriterView->rootModelNode()); QVERIFY(rootModelNode.isValid()); +#ifndef QDS_USE_PROJECTSTORAGE QCOMPARE(rootModelNode.type(), QmlDesigner::TypeName("QtQuick.Rectangle")); QCOMPARE(rootModelNode.metaInfo().majorVersion(), 2); QCOMPARE(rootModelNode.metaInfo().minorVersion(), 0); //QCOMPARE(rootModelNode.majorQtQuickVersion(), 2); QCOMPARE(rootModelNode.majorVersion(), 2); +#endif } void tst_TestCore::testQtQuickControls2() From 8b7762621b52778a40c603475acf18bc43e3a0a4 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 6 Mar 2024 19:37:43 +0100 Subject: [PATCH 150/176] QmlDesigner: Change model creation to new code model The model needs the project store, the path cache, the imports and the file path. So we now use a method in the model to copy them over to the new model. Task-number: QDS-12102 Change-Id: I80f911d55c5a5fdf9d1a87c1f4888e498086374b Reviewed-by: Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Tim Jenssen --- .../collectioneditor/datastoremodelnode.cpp | 7 +++++ .../componentcore/modelnodeoperations.cpp | 13 +++++++-- .../components/edit3d/bakelights.cpp | 9 +++++- .../components/eventlist/eventlist.cpp | 3 +- .../components/integration/designdocument.cpp | 4 ++- .../integration/designdocumentview.cpp | 16 ++++++++++ .../imagecache/imagecachecollector.cpp | 10 +++++++ .../imagecache/imagecachecollector.h | 4 +++ .../designercore/include/abstractview.h | 1 + .../qmldesigner/designercore/include/model.h | 29 ++++++++++++------- .../designercore/model/stylesheetmerger.cpp | 9 +++++- .../qmldesigner/coretests/tst_testcore.cpp | 5 +++- 12 files changed, 91 insertions(+), 19 deletions(-) diff --git a/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.cpp b/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.cpp index 503b3f1ea31..5be9c20f9ed 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.cpp @@ -145,6 +145,12 @@ void DataStoreModelNode::reloadModel() if (dataStoreQmlPath.exists() && dataStoreJsonPath.exists()) { if (!m_model.get() || m_model->fileUrl() != dataStoreQmlUrl) { +#ifdef QDS_USE_PROJECTSTORAGE + m_model = model()->createModel("JsonListModel"); + forceUpdate = true; + Import import = Import::createLibraryImport("QtQuick.Studio.Utils"); + m_model->changeImports({import}, {}); +#else m_model = Model::create(CollectionEditorConstants::JSONCOLLECTIONMODEL_TYPENAME, 1, 1); forceUpdate = true; Import import = Import::createLibraryImport( @@ -155,6 +161,7 @@ void DataStoreModelNode::reloadModel() } catch (const Exception &) { QTC_ASSERT(false, return); } +#endif } } else { reset(); diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp index 6ed1480d85c..9cd49177c8a 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp @@ -581,9 +581,14 @@ static void addSignal(const QString &typeName, const QString &itemId, const QString &signalName, bool isRootModelNode, - ExternalDependenciesInterface &externanDependencies) + ExternalDependenciesInterface &externanDependencies, + [[maybe_unused]] Model *otherModel) { +#ifdef QDS_USE_PROJECTSTORAGE + auto model = otherModel->createModel("Item"); +#else auto model = Model::create("Item", 2, 0); +#endif RewriterView rewriterView(externanDependencies, RewriterView::Amend); auto textEdit = qobject_cast @@ -709,14 +714,16 @@ void addSignalHandlerOrGotoImplementation(const SelectionContext &selectionState itemId, dialog->signal(), isModelNodeRoot, - selectionState.view()->externalDependencies()); + selectionState.view()->externalDependencies(), + selectionState.view()->model()); }); addSignal(typeName, itemId, dialog->signal(), isModelNodeRoot, - selectionState.view()->externalDependencies()); + selectionState.view()->externalDependencies(), + selectionState.view()->model()); //Move cursor to correct curser position const QString filePath = Core::EditorManager::currentDocument()->filePath().toString(); diff --git a/src/plugins/qmldesigner/components/edit3d/bakelights.cpp b/src/plugins/qmldesigner/components/edit3d/bakelights.cpp index 4e7c399b1c8..25a27f778bf 100644 --- a/src/plugins/qmldesigner/components/edit3d/bakelights.cpp +++ b/src/plugins/qmldesigner/components/edit3d/bakelights.cpp @@ -143,9 +143,12 @@ void BakeLights::bakeLights() m_rewriterView = new RewriterView{m_view->externalDependencies(), RewriterView::Amend}; m_nodeInstanceView = new NodeInstanceView{*m_connectionManager, m_view->externalDependencies()}; +#ifdef QDS_USE_PROJECTSTORAGE + m_model = m_view->model()->createModel("Item"); +#else m_model = QmlDesigner::Model::create("QtQuick/Item", 2, 1); m_model->setFileUrl(m_view->model()->fileUrl()); - +#endif // Take the current unsaved state of the main model and apply it to our copy auto textDocument = std::make_unique( m_view->model()->rewriterView()->textModifier()->textDocument()->toRawText()); @@ -252,7 +255,11 @@ void BakeLights::exposeModelsAndLights(const QString &nodeId) } RewriterView rewriter{m_view->externalDependencies(), RewriterView::Amend}; +#ifdef QDS_USE_PROJECTSTORAGE + auto compModel = m_view->model()->createModel("Item"); +#else ModelPointer compModel = QmlDesigner::Model::create("QtQuick/Item", 2, 1); +#endif const Utils::FilePath compFilePath = Utils::FilePath::fromString(componentFilePath); QByteArray src = compFilePath.fileContents().value(); diff --git a/src/plugins/qmldesigner/components/eventlist/eventlist.cpp b/src/plugins/qmldesigner/components/eventlist/eventlist.cpp index 5ba29bb0dff..22de112b6db 100644 --- a/src/plugins/qmldesigner/components/eventlist/eventlist.cpp +++ b/src/plugins/qmldesigner/components/eventlist/eventlist.cpp @@ -187,7 +187,7 @@ void EventList::initialize(EventListPluginView *parent) QByteArray unqualifiedTypeName = "ListModel"; auto metaInfo = parent->model()->metaInfo(unqualifiedTypeName); #ifdef QDS_USE_PROJECTSTORAGE - m_model = Model::create(unqualifiedTypeName, -1, -1); + m_model = parent->model()->createModel(unqualifiedTypeName); #else QByteArray fullTypeName = metaInfo.typeName(); int minorVersion = metaInfo.minorVersion(); @@ -195,7 +195,6 @@ void EventList::initialize(EventListPluginView *parent) m_model = Model::create(fullTypeName, majorVersion, minorVersion); #endif - m_model->setParent(parent); } if (!m_eventView) { diff --git a/src/plugins/qmldesigner/components/integration/designdocument.cpp b/src/plugins/qmldesigner/components/integration/designdocument.cpp index a82f5744efa..c0bebbb82bd 100644 --- a/src/plugins/qmldesigner/components/integration/designdocument.cpp +++ b/src/plugins/qmldesigner/components/integration/designdocument.cpp @@ -162,13 +162,15 @@ const AbstractView *DesignDocument::view() const ModelPointer DesignDocument::createInFileComponentModel() { +#ifdef QDS_USE_PROJECTSTORAGE + auto model = m_documentModel->createModel("Item", std::make_unique()); +#else auto model = Model::create("QtQuick.Item", 1, 0, nullptr, std::make_unique()); model->setFileUrl(m_documentModel->fileUrl()); -#ifndef QDS_USE_PROJECTSTORAGE model->setMetaInfo(m_documentModel->metaInfo()); #endif diff --git a/src/plugins/qmldesigner/components/integration/designdocumentview.cpp b/src/plugins/qmldesigner/components/integration/designdocumentview.cpp index 9d0deef713b..6ef95bf4c45 100644 --- a/src/plugins/qmldesigner/components/integration/designdocumentview.cpp +++ b/src/plugins/qmldesigner/components/integration/designdocumentview.cpp @@ -97,8 +97,12 @@ static bool hasOnly3DNodes(const ModelNode &node) QString DesignDocumentView::toText() const { +#ifdef QDS_USE_PROJECTSTORAGE + auto outputModel = model()->createModel("Rectangle"); +#else auto outputModel = Model::create("QtQuick.Rectangle", 1, 0, model()); outputModel->setFileUrl(model()->fileUrl()); +#endif QPlainTextEdit textEdit; QString imports; @@ -136,8 +140,12 @@ QString DesignDocumentView::toText() const void DesignDocumentView::fromText(const QString &text) { +#ifdef QDS_USE_PROJECTSTORAGE + auto inputModel = model()->createModel("Rectangle"); +#else auto inputModel = Model::create("QtQuick.Rectangle", 1, 0, model()); inputModel->setFileUrl(model()->fileUrl()); +#endif QPlainTextEdit textEdit; QString imports; const auto modelImports = model()->imports(); @@ -179,12 +187,16 @@ ModelPointer DesignDocumentView::pasteToModel(ExternalDependenciesInterface &ext QTC_ASSERT(parentModel, return nullptr); +#ifdef QDS_USE_PROJECTSTORAGE + auto pasteModel = parentModel->createModel("Item"); +#else auto pasteModel = Model::create("empty", 1, 0, parentModel); Q_ASSERT(pasteModel); if (!pasteModel) return nullptr; +#endif pasteModel->setFileUrl(parentModel->fileUrl()); pasteModel->changeImports(parentModel->imports(), {}); @@ -204,12 +216,16 @@ void DesignDocumentView::copyModelNodes(const QList &nodesToCopy, QTC_ASSERT(parentModel, return); +#ifdef QDS_USE_PROJECTSTORAGE + auto copyModel = parentModel->createModel("Rectangle"); +#else auto copyModel = Model::create("QtQuick.Rectangle", 1, 0, parentModel); copyModel->setFileUrl(parentModel->fileUrl()); copyModel->changeImports(parentModel->imports(), {}); Q_ASSERT(copyModel); +#endif QList selectedNodes = nodesToCopy; diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp b/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp index 9d32b803cba..955e676d3ba 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp +++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp @@ -76,6 +76,9 @@ void ImageCacheCollector::start(Utils::SmallStringView name, AbortCallback abortCallback, ImageCache::TraceToken traceToken) { + if (!m_projectStorage || !m_pathCache) + return; + using namespace NanotraceHR::Literals; auto [collectorTraceToken, flowtoken] = traceToken.beginDurationWithFlow( "generate image in standard collector"_t); @@ -86,8 +89,15 @@ void ImageCacheCollector::start(Utils::SmallStringView name, captureImageMaximumSize); const QString filePath{name}; +#ifdef QDS_USE_PROJECTSTORAGE + auto model = QmlDesigner::Model::create({*m_projectStorage, *m_pathCache}, + "Item", + {}, + QUrl::fromLocalFile(filePath)); +#else auto model = QmlDesigner::Model::create("QtQuick/Item", 2, 1); model->setFileUrl(QUrl::fromLocalFile(filePath)); +#endif auto textDocument = std::make_unique(fileToString(filePath)); diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.h b/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.h index e5230ea2b23..c2a912cc3ae 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.h +++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.h @@ -5,6 +5,8 @@ #include "imagecachecollectorinterface.h" +#include + #include QT_BEGIN_NAMESPACE @@ -62,6 +64,8 @@ private: QSize captureImageMaximumSize; ExternalDependenciesInterface &m_externalDependencies; ImageCacheCollectorNullImageHandling nullImageHandling{}; + ProjectStorageType *m_projectStorage = nullptr; + PathCacheType *m_pathCache = nullptr; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/include/abstractview.h b/src/plugins/qmldesigner/designercore/include/abstractview.h index f4565633482..450a51afdd0 100644 --- a/src/plugins/qmldesigner/designercore/include/abstractview.h +++ b/src/plugins/qmldesigner/designercore/include/abstractview.h @@ -86,6 +86,7 @@ public: ModelNode createModelNode(const TypeName &typeName); +# ModelNode createModelNode(const TypeName &typeName, int majorVersion, int minorVersion, diff --git a/src/plugins/qmldesigner/designercore/include/model.h b/src/plugins/qmldesigner/designercore/include/model.h index 1f6f6dbcad8..b907e6c5d8c 100644 --- a/src/plugins/qmldesigner/designercore/include/model.h +++ b/src/plugins/qmldesigner/designercore/include/model.h @@ -20,6 +20,13 @@ #include +#ifdef QDS_USE_PROJECTSTORAGE +# define DEPRECATED_OLD_CREATE_MODELNODE \ + [[deprecated("Use unqualified type names and no versions!")]] +#else +# define DEPRECATED_OLD_CREATE_MODELNODE +#endif + QT_BEGIN_NAMESPACE class QPixmap; class QUrl; @@ -87,11 +94,12 @@ public: ~Model(); - static ModelPointer create(const TypeName &typeName, - int major = 1, - int minor = 1, - Model *metaInfoProxyModel = nullptr, - std::unique_ptr resourceManagement = {}) + DEPRECATED_OLD_CREATE_MODELNODE static ModelPointer create( + const TypeName &typeName, + int major = 1, + int minor = 1, + Model *metaInfoProxyModel = nullptr, + std::unique_ptr resourceManagement = {}) { return ModelPointer( new Model(typeName, major, minor, metaInfoProxyModel, std::move(resourceManagement))); @@ -111,11 +119,12 @@ public: std::move(resourceManagement))); } - static ModelPointer create(ProjectStorageDependencies projectStorageDependencies, - const TypeName &typeName, - int major = 1, - int minor = 1, - std::unique_ptr resourceManagement = {}) + DEPRECATED_OLD_CREATE_MODELNODE static ModelPointer create( + ProjectStorageDependencies projectStorageDependencies, + const TypeName &typeName, + int major = 1, + int minor = 1, + std::unique_ptr resourceManagement = {}) { return ModelPointer(new Model(projectStorageDependencies, typeName, diff --git a/src/plugins/qmldesigner/designercore/model/stylesheetmerger.cpp b/src/plugins/qmldesigner/designercore/model/stylesheetmerger.cpp index 8e0416b68aa..16877b61db6 100644 --- a/src/plugins/qmldesigner/designercore/model/stylesheetmerger.cpp +++ b/src/plugins/qmldesigner/designercore/model/stylesheetmerger.cpp @@ -621,7 +621,11 @@ void StylesheetMerger::styleMerge(const QString &qmlTemplateString, QTC_ASSERT(parentModel, return ); +#ifdef QDS_USE_PROJECTSTORAGE + auto templateModel = model->createModel("Item"); +#else auto templateModel(Model::create("QtQuick.Item", 2, 1, parentModel)); +#endif Q_ASSERT(templateModel.get()); templateModel->setFileUrl(parentModel->fileUrl()); @@ -647,9 +651,12 @@ void StylesheetMerger::styleMerge(const QString &qmlTemplateString, ModelNode templateRootNode = templateRewriterView->rootModelNode(); QTC_ASSERT(templateRootNode.isValid(), return ); +#ifdef QDS_USE_PROJECTSTORAGE + auto styleModel = model->createModel("Item"); +#else auto styleModel(Model::create("QtQuick.Item", 2, 1, parentModel)); Q_ASSERT(styleModel.get()); - +#endif styleModel->setFileUrl(parentModel->fileUrl()); QPlainTextEdit textEditStyle; diff --git a/tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp b/tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp index ca82e95b8c1..569d0ce3a4e 100644 --- a/tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp +++ b/tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp @@ -182,8 +182,11 @@ ModelPointer createModel(const QString &typeName, { QApplication::processEvents(); +#ifdef QDS_USE_PROJECTSTORAGE + auto model = metaInfoPropxyModel->createModel(typeName.toUtf8()); +#else auto model = QmlDesigner::Model::create(typeName.toUtf8(), major, minor, metaInfoPropxyModel); - +#endif QPlainTextEdit *textEdit = new QPlainTextEdit; QObject::connect(model.get(), &QObject::destroyed, textEdit, &QObject::deleteLater); textEdit->setPlainText(QString("import %1 %3.%4; %2{}") From 3cc4e22cdc620a2f7c509d155eeddbf236ac8fa6 Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Wed, 28 Feb 2024 09:21:15 +0100 Subject: [PATCH 151/176] Utils: Fix build on macOS < 13 with C++20 The Clang implementation of span is incomplete until LLVM 15 / Xcode 15 / macOS 13.5 Change-Id: Iac8514fc84643cb4dae9cfaac603df5e798f9ff4 Reviewed-by: Marco Bubke Reviewed-by: (cherry picked from commit db5ac2dc15f0ac607c03416e538ae2ffc5482467) Reviewed-by: Tim Jenssen Reviewed-by: Eike Ziller Reviewed-by: Qt CI Patch Build Bot --- src/libs/utils/span.h | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/libs/utils/span.h b/src/libs/utils/span.h index b1fba814472..d21bb9ab00b 100644 --- a/src/libs/utils/span.h +++ b/src/libs/utils/span.h @@ -5,7 +5,9 @@ #include -#if __cplusplus >= 202002L +// The (Apple) Clang implementation of span is incomplete until LLVM 15 / Xcode 14.3 / macOS 13 +#if __cplusplus >= 202002L \ + && !(defined(__apple_build_version__) && __apple_build_version__ < 14030022) #include namespace Utils { @@ -22,6 +24,11 @@ QT_WARNING_PUSH #elif defined(Q_CC_GNU) || defined(Q_CC_CLANG) #pragma GCC system_header #endif + +// disable automatic usage of std::span in span-lite +// since we make that decision ourselves at the top of this header +#define span_CONFIG_SELECT_SPAN span_SPAN_NONSTD + #include <3rdparty/span/span.hpp> namespace Utils { using namespace nonstd; From 3dbdad20dcb92b5bca757c4c4cebf09badb6bf00 Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Tue, 27 Feb 2024 16:08:45 +0100 Subject: [PATCH 152/176] Utils: Fix build with MSVC with C++20 Rename process.h back to qtcprocess.h MSVC's "threads" standard header includes , and that ends up including our process.h from Utils. There already was a hacky workaround in place for a similar issue with MINGW, but that doesn't work with MSVC because that doesn't have Simply use a name that doesn't conflict. Change-Id: I1159cd2096b4f2dbc4a1728d0131dd6edd30ebd3 Reviewed-by: Qt CI Bot Reviewed-by: Reviewed-by: hjk (cherry picked from commit 5af531cd39067366cea627e3db1a0b83d7172873) Reviewed-by: Tim Jenssen Reviewed-by: Eike Ziller --- src/libs/extensionsystem/pluginmanager.cpp | 2 +- src/libs/qmljs/qmljsplugindumper.cpp | 2 +- src/libs/utils/CMakeLists.txt | 2 +- src/libs/utils/buildablehelperlibrary.cpp | 2 +- src/libs/utils/clangutils.cpp | 2 +- src/libs/utils/devicefileaccess.cpp | 2 +- src/libs/utils/deviceshell.cpp | 2 +- src/libs/utils/externalterminalprocessimpl.cpp | 2 +- src/libs/utils/filestreamer.cpp | 2 +- src/libs/utils/pathchooser.cpp | 2 +- src/libs/utils/processinfo.cpp | 2 +- src/libs/utils/{process.cpp => qtcprocess.cpp} | 4 ++-- src/libs/utils/{process.h => qtcprocess.h} | 8 +------- src/libs/utils/terminalhooks.cpp | 2 +- src/libs/utils/unarchiver.h | 2 +- src/libs/utils/utils.qbs | 4 ++-- src/plugins/android/androidavdmanager.cpp | 2 +- src/plugins/android/androidbuildapkstep.cpp | 2 +- src/plugins/android/androidconfigurations.cpp | 2 +- src/plugins/android/androidcreatekeystorecertificate.cpp | 2 +- src/plugins/android/androiddebugsupport.cpp | 2 +- src/plugins/android/androiddeployqtstep.cpp | 2 +- src/plugins/android/androiddevice.cpp | 2 +- src/plugins/android/androidmanager.cpp | 2 +- src/plugins/android/androidpackageinstallationstep.cpp | 2 +- src/plugins/android/androidqmlpreviewworker.cpp | 2 +- src/plugins/android/androidrunconfiguration.cpp | 2 +- src/plugins/android/androidrunnerworker.cpp | 2 +- src/plugins/android/androidsdkmanager.cpp | 2 +- src/plugins/android/androidsettingswidget.cpp | 2 +- src/plugins/android/androidsignaloperation.cpp | 2 +- src/plugins/autotest/boost/boosttestoutputreader.cpp | 2 +- src/plugins/autotest/gtest/gtestoutputreader.cpp | 2 +- src/plugins/autotest/testoutputreader.cpp | 2 +- src/plugins/autotest/testrunner.cpp | 2 +- src/plugins/autotoolsprojectmanager/autogenstep.cpp | 2 +- src/plugins/autotoolsprojectmanager/autoreconfstep.cpp | 2 +- src/plugins/autotoolsprojectmanager/configurestep.cpp | 2 +- src/plugins/autotoolsprojectmanager/makefileparser.cpp | 2 +- src/plugins/baremetal/baremetaldebugsupport.cpp | 2 +- .../baremetal/debugservers/gdb/jlinkgdbserverprovider.cpp | 2 +- .../debugservers/gdb/openocdgdbserverprovider.cpp | 2 +- .../baremetal/debugservers/uvsc/uvscserverprovider.h | 2 +- src/plugins/baremetal/iarewtoolchain.cpp | 2 +- src/plugins/baremetal/keiltoolchain.cpp | 2 +- src/plugins/baremetal/sdcctoolchain.cpp | 2 +- src/plugins/beautifier/artisticstyle/artisticstyle.cpp | 2 +- src/plugins/beautifier/beautifiertool.cpp | 2 +- src/plugins/beautifier/uncrustify/uncrustify.cpp | 2 +- src/plugins/boot2qt/device-detection/qdbwatcher.cpp | 2 +- src/plugins/boot2qt/qdbdevice.cpp | 2 +- src/plugins/boot2qt/qdbdevicedebugsupport.cpp | 2 +- src/plugins/boot2qt/qdbmakedefaultappstep.cpp | 2 +- src/plugins/boot2qt/qdbplugin.cpp | 2 +- src/plugins/boot2qt/qdbstopapplicationstep.cpp | 2 +- src/plugins/clangtools/clangtoolrunner.cpp | 2 +- src/plugins/clangtools/clangtoolsutils.cpp | 2 +- src/plugins/clangtools/executableinfo.cpp | 2 +- src/plugins/clearcase/clearcaseplugin.cpp | 2 +- src/plugins/clearcase/clearcasesync.cpp | 2 +- src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp | 2 +- src/plugins/cmakeprojectmanager/cmakeprocess.cpp | 2 +- src/plugins/cmakeprojectmanager/cmakeprojectimporter.cpp | 2 +- src/plugins/cmakeprojectmanager/cmaketool.cpp | 2 +- src/plugins/cmakeprojectmanager/fileapidataextractor.cpp | 2 +- src/plugins/coreplugin/dialogs/externaltoolconfig.cpp | 2 +- src/plugins/coreplugin/externaltool.cpp | 2 +- src/plugins/coreplugin/fileutils.cpp | 2 +- src/plugins/coreplugin/locator/executefilter.cpp | 2 +- src/plugins/coreplugin/locator/spotlightlocatorfilter.cpp | 2 +- src/plugins/coreplugin/patchtool.cpp | 2 +- src/plugins/coreplugin/plugininstallwizard.cpp | 2 +- .../coreplugin/progressmanager/processprogress.cpp | 2 +- src/plugins/cppcheck/cppcheckrunner.h | 2 +- src/plugins/cppeditor/cppcodemodelsettings.cpp | 3 ++- src/plugins/cppeditor/cppmodelmanager.cpp | 2 +- src/plugins/debugger/cdb/cdbengine.cpp | 2 +- src/plugins/debugger/cdb/cdbengine.h | 2 +- src/plugins/debugger/dap/dapclient.cpp | 2 +- src/plugins/debugger/dap/dapclient.h | 2 +- src/plugins/debugger/dap/dapengine.cpp | 2 +- src/plugins/debugger/dap/dapengine.h | 2 +- src/plugins/debugger/debuggerengine.cpp | 2 +- src/plugins/debugger/debuggeritem.cpp | 2 +- src/plugins/debugger/debuggeritemmanager.cpp | 2 +- src/plugins/debugger/debuggerruncontrol.cpp | 2 +- src/plugins/debugger/debuggersourcepathmappingwidget.cpp | 2 +- src/plugins/debugger/gdb/gdbengine.cpp | 2 +- src/plugins/debugger/gdb/gdbengine.h | 2 +- src/plugins/debugger/lldb/lldbengine.cpp | 2 +- src/plugins/debugger/lldb/lldbengine.h | 2 +- src/plugins/debugger/moduleshandler.cpp | 2 +- src/plugins/debugger/pdb/pdbengine.cpp | 2 +- src/plugins/debugger/pdb/pdbengine.h | 2 +- src/plugins/debugger/qml/qmlengine.cpp | 2 +- src/plugins/debugger/terminal.cpp | 2 +- src/plugins/docker/dockerapi.cpp | 2 +- src/plugins/docker/dockerdevice.cpp | 2 +- src/plugins/effectcomposer/effectcomposermodel.cpp | 2 +- src/plugins/fakevim/fakevimplugin.cpp | 2 +- src/plugins/fossil/fossilclient.cpp | 2 +- src/plugins/genericprojectmanager/genericproject.cpp | 2 +- src/plugins/git/branchmodel.cpp | 2 +- src/plugins/git/branchview.cpp | 2 +- src/plugins/git/changeselectiondialog.cpp | 2 +- src/plugins/git/gerrit/gerritmodel.cpp | 2 +- src/plugins/git/gerrit/gerritplugin.cpp | 2 +- src/plugins/git/gitclient.cpp | 2 +- src/plugins/git/gitgrep.cpp | 2 +- src/plugins/git/mergetool.h | 2 +- src/plugins/gitlab/gitlabclonedialog.cpp | 2 +- src/plugins/gitlab/queryrunner.h | 2 +- src/plugins/haskell/haskellmanager.cpp | 2 +- src/plugins/incredibuild/cmakecommandbuilder.cpp | 2 +- src/plugins/ios/iosbuildstep.cpp | 2 +- src/plugins/ios/iosconfigurations.cpp | 2 +- src/plugins/ios/iosdeploystep.cpp | 2 +- src/plugins/ios/iosdevice.cpp | 2 +- src/plugins/ios/iosdsymbuildstep.cpp | 2 +- src/plugins/ios/iosprobe.cpp | 2 +- src/plugins/ios/iosrunconfiguration.cpp | 2 +- src/plugins/ios/iosrunner.cpp | 2 +- src/plugins/ios/iossimulator.cpp | 2 +- src/plugins/ios/iostoolhandler.cpp | 2 +- src/plugins/ios/simulatorcontrol.cpp | 2 +- src/plugins/languageclient/client.cpp | 2 +- src/plugins/languageclient/languageclientinterface.h | 2 +- src/plugins/languageclient/languageclientutils.cpp | 2 +- src/plugins/mcusupport/mcuqmlprojectnode.h | 2 +- src/plugins/mcusupport/mcusupportversiondetection.cpp | 2 +- src/plugins/mercurial/mercurialclient.cpp | 2 +- .../mesonprojectmanager/mesonbuildconfiguration.cpp | 2 +- src/plugins/mesonprojectmanager/mesonwrapper.h | 2 +- src/plugins/mesonprojectmanager/toolwrapper.cpp | 2 +- src/plugins/nim/project/nimblebuildsystem.cpp | 2 +- src/plugins/nim/project/nimcompilerbuildstep.cpp | 2 +- src/plugins/nim/project/nimtoolchain.cpp | 2 +- src/plugins/nim/suggest/server.h | 2 +- src/plugins/perforce/perforcechecker.h | 2 +- src/plugins/perforce/perforceplugin.cpp | 2 +- src/plugins/perfprofiler/perfprofilerruncontrol.cpp | 2 +- src/plugins/perfprofiler/perfsettings.cpp | 2 +- src/plugins/perfprofiler/perftracepointdialog.cpp | 2 +- src/plugins/projectexplorer/abstractprocessstep.cpp | 2 +- .../customwizard/customwizardscriptgenerator.cpp | 2 +- .../projectexplorer/devicesupport/desktopdevice.cpp | 2 +- .../projectexplorer/devicesupport/devicemanager.cpp | 2 +- .../devicesupport/deviceusedportsgatherer.cpp | 2 +- .../projectexplorer/devicesupport/sshparameters.cpp | 2 +- src/plugins/projectexplorer/extracompiler.cpp | 2 +- src/plugins/projectexplorer/gcctoolchain.cpp | 2 +- src/plugins/projectexplorer/makestep.cpp | 2 +- src/plugins/projectexplorer/msvctoolchain.cpp | 2 +- src/plugins/projectexplorer/processparameters.cpp | 2 +- src/plugins/projectexplorer/projectmodels.cpp | 2 +- src/plugins/projectexplorer/runconfigurationaspects.cpp | 2 +- src/plugins/projectexplorer/runcontrol.cpp | 2 +- src/plugins/projectexplorer/targetsetuppage.cpp | 2 +- src/plugins/projectexplorer/taskfile.cpp | 2 +- src/plugins/projectexplorer/toolchainconfigwidget.cpp | 2 +- src/plugins/projectexplorer/userfileaccessor.cpp | 2 +- src/plugins/python/pipsupport.cpp | 2 +- src/plugins/python/pipsupport.h | 2 +- src/plugins/python/pyside.cpp | 2 +- src/plugins/python/pysideuicextracompiler.cpp | 2 +- src/plugins/python/pythonbuildconfiguration.cpp | 2 +- src/plugins/python/pythonkitaspect.cpp | 2 +- src/plugins/python/pythonlanguageclient.cpp | 2 +- src/plugins/python/pythonsettings.cpp | 2 +- src/plugins/python/pythonutils.cpp | 2 +- src/plugins/qbsprojectmanager/qbsbuildconfiguration.cpp | 2 +- src/plugins/qbsprojectmanager/qbsprofilemanager.cpp | 2 +- src/plugins/qbsprojectmanager/qbssession.cpp | 2 +- src/plugins/qbsprojectmanager/qbssettings.cpp | 2 +- .../qmakeprojectmanager/librarydetailscontroller.cpp | 2 +- src/plugins/qmakeprojectmanager/makefileparse.cpp | 2 +- .../qmakeprojectmanager/qmakebuildconfiguration.cpp | 2 +- src/plugins/qmakeprojectmanager/qmakemakestep.cpp | 2 +- src/plugins/qmakeprojectmanager/qmakeparsernodes.cpp | 2 +- src/plugins/qmakeprojectmanager/qmakeproject.cpp | 2 +- src/plugins/qmakeprojectmanager/qmakeprojectimporter.cpp | 2 +- src/plugins/qmakeprojectmanager/qmakestep.cpp | 2 +- .../components/componentcore/modelnodeoperations.cpp | 2 +- .../components/componentcore/resourcegenerator.cpp | 2 +- .../designercore/instances/nodeinstanceview.cpp | 2 +- src/plugins/qmlpreview/qmlpreviewruncontrol.cpp | 2 +- src/plugins/qmlprofiler/qmlprofilerruncontrol.cpp | 2 +- src/plugins/qmlprojectmanager/qmlproject.cpp | 2 +- src/plugins/qmlprojectmanager/qmlprojectplugin.cpp | 2 +- .../qmlprojectmanager/qmlprojectrunconfiguration.cpp | 2 +- src/plugins/qnx/qnxanalyzesupport.cpp | 2 +- src/plugins/qnx/qnxdebugsupport.cpp | 2 +- src/plugins/qnx/qnxdeployqtlibrariesdialog.cpp | 2 +- src/plugins/qnx/qnxdevice.cpp | 2 +- src/plugins/qnx/qnxdevicetester.cpp | 2 +- src/plugins/qnx/qnxutils.cpp | 2 +- src/plugins/qnx/slog2inforunner.cpp | 2 +- .../qtapplicationmanager/appmanagerinstallpackagestep.cpp | 2 +- src/plugins/qtapplicationmanager/appmanagerruncontrol.cpp | 2 +- src/plugins/qtsupport/baseqtversion.cpp | 2 +- src/plugins/qtsupport/externaleditors.cpp | 2 +- src/plugins/qtsupport/qtsupportplugin.cpp | 2 +- src/plugins/qtsupport/qtversionmanager.cpp | 2 +- src/plugins/qtsupport/uicgenerator.cpp | 2 +- src/plugins/remotelinux/customcommanddeploystep.cpp | 2 +- src/plugins/remotelinux/filesystemaccess_test.cpp | 2 +- src/plugins/remotelinux/genericdeploystep.cpp | 2 +- src/plugins/remotelinux/genericdirectuploadstep.cpp | 2 +- src/plugins/remotelinux/linuxdevice.cpp | 2 +- src/plugins/remotelinux/linuxdevicetester.cpp | 2 +- src/plugins/remotelinux/makeinstallstep.cpp | 2 +- src/plugins/remotelinux/publickeydeploymentdialog.cpp | 2 +- src/plugins/remotelinux/remotelinuxsignaloperation.cpp | 2 +- src/plugins/remotelinux/sshkeycreationdialog.cpp | 2 +- src/plugins/remotelinux/tarpackagedeploystep.cpp | 2 +- src/plugins/screenrecorder/cropandtrim.cpp | 2 +- src/plugins/screenrecorder/export.cpp | 2 +- src/plugins/screenrecorder/ffmpegutils.cpp | 2 +- src/plugins/screenrecorder/record.cpp | 2 +- src/plugins/silversearcher/findinfilessilversearcher.cpp | 2 +- src/plugins/squish/objectsmapdocument.cpp | 2 +- src/plugins/squish/squishprocessbase.h | 2 +- src/plugins/squish/squishtools.h | 2 +- src/plugins/subversion/subversionclient.cpp | 2 +- src/plugins/terminal/shellintegration.h | 2 +- src/plugins/terminal/terminalwidget.h | 2 +- src/plugins/texteditor/basefilefind.cpp | 2 +- src/plugins/texteditor/formattexteditor.cpp | 2 +- src/plugins/updateinfo/updateinfoplugin.cpp | 2 +- src/plugins/valgrind/callgrindengine.cpp | 2 +- src/plugins/valgrind/callgrindengine.h | 2 +- src/plugins/valgrind/callgrindtool.cpp | 2 +- src/plugins/valgrind/memchecktool.cpp | 2 +- src/plugins/valgrind/valgrindmemcheckparsertest.cpp | 2 +- src/plugins/valgrind/valgrindprocess.cpp | 2 +- src/plugins/vcsbase/vcsbaseclient.cpp | 2 +- src/plugins/vcsbase/vcsbasediffeditorcontroller.cpp | 2 +- src/plugins/vcsbase/vcsbaseplugin.cpp | 2 +- src/plugins/vcsbase/vcsbasesubmiteditor.cpp | 2 +- src/plugins/vcsbase/vcscommand.cpp | 2 +- src/plugins/vcsbase/vcsoutputwindow.cpp | 2 +- src/plugins/webassembly/webassemblyemsdk.cpp | 2 +- src/plugins/webassembly/webassemblyrunconfiguration.cpp | 2 +- tests/auto/utils/commandline/tst_commandline.cpp | 2 +- tests/auto/utils/deviceshell/tst_deviceshell.cpp | 2 +- tests/auto/utils/process/processtestapp/main.cpp | 2 +- .../auto/utils/process/processtestapp/processtestapp.cpp | 2 +- tests/auto/utils/process/tst_process.cpp | 2 +- tests/manual/deviceshell/tst_deviceshell.cpp | 2 +- 249 files changed, 252 insertions(+), 257 deletions(-) rename src/libs/utils/{process.cpp => qtcprocess.cpp} (99%) rename src/libs/utils/{process.h => qtcprocess.h} (96%) diff --git a/src/libs/extensionsystem/pluginmanager.cpp b/src/libs/extensionsystem/pluginmanager.cpp index fcb3062faa9..4254fdfc0bf 100644 --- a/src/libs/extensionsystem/pluginmanager.cpp +++ b/src/libs/extensionsystem/pluginmanager.cpp @@ -18,7 +18,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/src/libs/qmljs/qmljsplugindumper.cpp b/src/libs/qmljs/qmljsplugindumper.cpp index 36f2cd802db..653fa8a7013 100644 --- a/src/libs/qmljs/qmljsplugindumper.cpp +++ b/src/libs/qmljs/qmljsplugindumper.cpp @@ -13,7 +13,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/libs/utils/CMakeLists.txt b/src/libs/utils/CMakeLists.txt index eb2eaed9ddc..3fc4b0bf583 100644 --- a/src/libs/utils/CMakeLists.txt +++ b/src/libs/utils/CMakeLists.txt @@ -123,7 +123,6 @@ add_qtc_library(Utils port.cpp port.h portlist.cpp portlist.h predicates.h - process.cpp process.h processenums.h processhandle.cpp processhandle.h processhelper.cpp processhelper.h @@ -136,6 +135,7 @@ add_qtc_library(Utils qrcparser.cpp qrcparser.h qtcassert.cpp qtcassert.h qtcolorbutton.cpp qtcolorbutton.h + qtcprocess.cpp qtcprocess.h qtcsettings.cpp qtcsettings.h ranges.h reloadpromptutils.cpp reloadpromptutils.h diff --git a/src/libs/utils/buildablehelperlibrary.cpp b/src/libs/utils/buildablehelperlibrary.cpp index 3b54d11412b..2be40b6c9bf 100644 --- a/src/libs/utils/buildablehelperlibrary.cpp +++ b/src/libs/utils/buildablehelperlibrary.cpp @@ -4,7 +4,7 @@ #include "buildablehelperlibrary.h" #include "environment.h" #include "hostosinfo.h" -#include "process.h" +#include "qtcprocess.h" #include #include diff --git a/src/libs/utils/clangutils.cpp b/src/libs/utils/clangutils.cpp index 6cf265820e7..4c8c7d801db 100644 --- a/src/libs/utils/clangutils.cpp +++ b/src/libs/utils/clangutils.cpp @@ -4,7 +4,7 @@ #include "clangutils.h" #include "filepath.h" -#include "process.h" +#include "qtcprocess.h" #include "utilstr.h" #include diff --git a/src/libs/utils/devicefileaccess.cpp b/src/libs/utils/devicefileaccess.cpp index a38a098d380..2769f592816 100644 --- a/src/libs/utils/devicefileaccess.cpp +++ b/src/libs/utils/devicefileaccess.cpp @@ -13,7 +13,7 @@ #include "utilstr.h" #ifndef UTILS_STATIC_LIBRARY -#include "process.h" +#include "qtcprocess.h" #endif #include diff --git a/src/libs/utils/deviceshell.cpp b/src/libs/utils/deviceshell.cpp index abccaa1b56b..69abc5ce4a4 100644 --- a/src/libs/utils/deviceshell.cpp +++ b/src/libs/utils/deviceshell.cpp @@ -3,7 +3,7 @@ #include "deviceshell.h" -#include "process.h" +#include "qtcprocess.h" #include "processinterface.h" #include "qtcassert.h" #include "utilstr.h" diff --git a/src/libs/utils/externalterminalprocessimpl.cpp b/src/libs/utils/externalterminalprocessimpl.cpp index ca178d7bf9c..41b7259b8f5 100644 --- a/src/libs/utils/externalterminalprocessimpl.cpp +++ b/src/libs/utils/externalterminalprocessimpl.cpp @@ -4,7 +4,7 @@ #include "externalterminalprocessimpl.h" #include "algorithm.h" -#include "process.h" +#include "qtcprocess.h" #include "terminalcommand.h" #include "utilstr.h" diff --git a/src/libs/utils/filestreamer.cpp b/src/libs/utils/filestreamer.cpp index 2fd40079242..663f505b881 100644 --- a/src/libs/utils/filestreamer.cpp +++ b/src/libs/utils/filestreamer.cpp @@ -4,7 +4,7 @@ #include "filestreamer.h" #include "async.h" -#include "process.h" +#include "qtcprocess.h" #include #include diff --git a/src/libs/utils/pathchooser.cpp b/src/libs/utils/pathchooser.cpp index 437b94bb5cd..a4d2a060884 100644 --- a/src/libs/utils/pathchooser.cpp +++ b/src/libs/utils/pathchooser.cpp @@ -11,7 +11,7 @@ #include "hostosinfo.h" #include "macroexpander.h" #include "optionpushbutton.h" -#include "process.h" +#include "qtcprocess.h" #include "qtcassert.h" #include "utilstr.h" diff --git a/src/libs/utils/processinfo.cpp b/src/libs/utils/processinfo.cpp index 499745473ee..0717bddbe9e 100644 --- a/src/libs/utils/processinfo.cpp +++ b/src/libs/utils/processinfo.cpp @@ -4,7 +4,7 @@ #include "processinfo.h" #include "algorithm.h" -#include "process.h" +#include "qtcprocess.h" #include #include diff --git a/src/libs/utils/process.cpp b/src/libs/utils/qtcprocess.cpp similarity index 99% rename from src/libs/utils/process.cpp rename to src/libs/utils/qtcprocess.cpp index 5bf44513654..2ebc6b351d4 100644 --- a/src/libs/utils/process.cpp +++ b/src/libs/utils/qtcprocess.cpp @@ -1,7 +1,7 @@ // Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -#include "process.h" +#include "qtcprocess.h" #include "algorithm.h" #include "environment.h" @@ -2175,4 +2175,4 @@ void ProcessTaskAdapter::start() } // namespace Utils -#include "process.moc" +#include "qtcprocess.moc" diff --git a/src/libs/utils/process.h b/src/libs/utils/qtcprocess.h similarity index 96% rename from src/libs/utils/process.h rename to src/libs/utils/qtcprocess.h index 694c1a12a86..a7e00882a31 100644 --- a/src/libs/utils/process.h +++ b/src/libs/utils/qtcprocess.h @@ -1,11 +1,7 @@ // Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -#if defined(Q_CC_MINGW) && defined(WIN_PTHREADS_H) && !defined(_INC_PROCESS) - // Arrived here via which wants to include - #include_next -#elif !defined(UTILS_PROCESS_H) -#define UTILS_PROCESS_H +#pragma once #include "utils_global.h" @@ -223,5 +219,3 @@ public: using ProcessTask = Tasking::CustomTask; } // namespace Utils - -#endif // UTILS_PROCESS_H diff --git a/src/libs/utils/terminalhooks.cpp b/src/libs/utils/terminalhooks.cpp index 52bcc056295..3e5151e6f39 100644 --- a/src/libs/utils/terminalhooks.cpp +++ b/src/libs/utils/terminalhooks.cpp @@ -5,7 +5,7 @@ #include "externalterminalprocessimpl.h" #include "filepath.h" -#include "process.h" +#include "qtcprocess.h" #include "utilstr.h" #include diff --git a/src/libs/utils/unarchiver.h b/src/libs/utils/unarchiver.h index b255cd49900..61818318bb7 100644 --- a/src/libs/utils/unarchiver.h +++ b/src/libs/utils/unarchiver.h @@ -6,7 +6,7 @@ #include "utils_global.h" #include "commandline.h" -#include "process.h" +#include "qtcprocess.h" #include diff --git a/src/libs/utils/utils.qbs b/src/libs/utils/utils.qbs index 7bf55d8f247..6aff153d357 100644 --- a/src/libs/utils/utils.qbs +++ b/src/libs/utils/utils.qbs @@ -232,8 +232,8 @@ QtcLibrary { "portlist.cpp", "portlist.h", "predicates.h", - "process.cpp", - "process.h", + "qtcprocess.cpp", + "qtcprocess.h", "processenums.h", "processhandle.cpp", "processhandle.h", diff --git a/src/plugins/android/androidavdmanager.cpp b/src/plugins/android/androidavdmanager.cpp index a939fb59102..c37daf43b89 100644 --- a/src/plugins/android/androidavdmanager.cpp +++ b/src/plugins/android/androidavdmanager.cpp @@ -11,7 +11,7 @@ #include #include -#include +#include #include #include diff --git a/src/plugins/android/androidbuildapkstep.cpp b/src/plugins/android/androidbuildapkstep.cpp index 34c6a9b619a..dfd8f4d3cea 100644 --- a/src/plugins/android/androidbuildapkstep.cpp +++ b/src/plugins/android/androidbuildapkstep.cpp @@ -36,7 +36,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/plugins/android/androidconfigurations.cpp b/src/plugins/android/androidconfigurations.cpp index fae6c64bbe3..4180917d950 100644 --- a/src/plugins/android/androidconfigurations.cpp +++ b/src/plugins/android/androidconfigurations.cpp @@ -33,7 +33,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/src/plugins/android/androidcreatekeystorecertificate.cpp b/src/plugins/android/androidcreatekeystorecertificate.cpp index 7a6fd7cf593..77aed2a6ad4 100644 --- a/src/plugins/android/androidcreatekeystorecertificate.cpp +++ b/src/plugins/android/androidcreatekeystorecertificate.cpp @@ -7,7 +7,7 @@ #include #include -#include +#include #include #include diff --git a/src/plugins/android/androiddebugsupport.cpp b/src/plugins/android/androiddebugsupport.cpp index 2f016eb23a8..cea404ef6f2 100644 --- a/src/plugins/android/androiddebugsupport.cpp +++ b/src/plugins/android/androiddebugsupport.cpp @@ -21,7 +21,7 @@ #include #include -#include +#include #include #include diff --git a/src/plugins/android/androiddeployqtstep.cpp b/src/plugins/android/androiddeployqtstep.cpp index fcf5b9703ad..6e520d3721b 100644 --- a/src/plugins/android/androiddeployqtstep.cpp +++ b/src/plugins/android/androiddeployqtstep.cpp @@ -38,7 +38,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/plugins/android/androiddevice.cpp b/src/plugins/android/androiddevice.cpp index b3f99159243..1d87fa1ddd0 100644 --- a/src/plugins/android/androiddevice.cpp +++ b/src/plugins/android/androiddevice.cpp @@ -22,7 +22,7 @@ #include #include -#include +#include #include #include diff --git a/src/plugins/android/androidmanager.cpp b/src/plugins/android/androidmanager.cpp index b95b6bb4dd1..3666762be6f 100644 --- a/src/plugins/android/androidmanager.cpp +++ b/src/plugins/android/androidmanager.cpp @@ -23,7 +23,7 @@ #include #include -#include +#include #include #include diff --git a/src/plugins/android/androidpackageinstallationstep.cpp b/src/plugins/android/androidpackageinstallationstep.cpp index 0fea1e283cc..8f1d4b01d57 100644 --- a/src/plugins/android/androidpackageinstallationstep.cpp +++ b/src/plugins/android/androidpackageinstallationstep.cpp @@ -22,7 +22,7 @@ #include #include -#include +#include #include #include diff --git a/src/plugins/android/androidqmlpreviewworker.cpp b/src/plugins/android/androidqmlpreviewworker.cpp index 06a14edc06e..f194c6e9792 100644 --- a/src/plugins/android/androidqmlpreviewworker.cpp +++ b/src/plugins/android/androidqmlpreviewworker.cpp @@ -28,7 +28,7 @@ #include #include -#include +#include #include #include diff --git a/src/plugins/android/androidrunconfiguration.cpp b/src/plugins/android/androidrunconfiguration.cpp index 8a057a3faa1..323e5d45b46 100644 --- a/src/plugins/android/androidrunconfiguration.cpp +++ b/src/plugins/android/androidrunconfiguration.cpp @@ -16,7 +16,7 @@ #include #include -#include +#include #include #include diff --git a/src/plugins/android/androidrunnerworker.cpp b/src/plugins/android/androidrunnerworker.cpp index d18f645165f..ba68ac651d7 100644 --- a/src/plugins/android/androidrunnerworker.cpp +++ b/src/plugins/android/androidrunnerworker.cpp @@ -23,7 +23,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/src/plugins/android/androidsdkmanager.cpp b/src/plugins/android/androidsdkmanager.cpp index 029cfd83524..7ae9ce05aaa 100644 --- a/src/plugins/android/androidsdkmanager.cpp +++ b/src/plugins/android/androidsdkmanager.cpp @@ -9,7 +9,7 @@ #include #include -#include +#include #include #include diff --git a/src/plugins/android/androidsettingswidget.cpp b/src/plugins/android/androidsettingswidget.cpp index c75ba299c5b..8cffab66734 100644 --- a/src/plugins/android/androidsettingswidget.cpp +++ b/src/plugins/android/androidsettingswidget.cpp @@ -19,8 +19,8 @@ #include #include #include -#include #include +#include #include #include diff --git a/src/plugins/android/androidsignaloperation.cpp b/src/plugins/android/androidsignaloperation.cpp index 9b71a07a9b3..5016b2573e2 100644 --- a/src/plugins/android/androidsignaloperation.cpp +++ b/src/plugins/android/androidsignaloperation.cpp @@ -4,7 +4,7 @@ #include "androidconfigurations.h" #include "androidsignaloperation.h" -#include +#include #include using namespace Utils; diff --git a/src/plugins/autotest/boost/boosttestoutputreader.cpp b/src/plugins/autotest/boost/boosttestoutputreader.cpp index 7b58d74f528..c069de97014 100644 --- a/src/plugins/autotest/boost/boosttestoutputreader.cpp +++ b/src/plugins/autotest/boost/boosttestoutputreader.cpp @@ -9,7 +9,7 @@ #include "../autotesttr.h" #include "../testtreeitem.h" -#include +#include #include #include diff --git a/src/plugins/autotest/gtest/gtestoutputreader.cpp b/src/plugins/autotest/gtest/gtestoutputreader.cpp index b24291caeb5..c0c08196ada 100644 --- a/src/plugins/autotest/gtest/gtestoutputreader.cpp +++ b/src/plugins/autotest/gtest/gtestoutputreader.cpp @@ -8,7 +8,7 @@ #include "../autotesttr.h" #include -#include +#include #include diff --git a/src/plugins/autotest/testoutputreader.cpp b/src/plugins/autotest/testoutputreader.cpp index cef36c90e76..727feeac7d3 100644 --- a/src/plugins/autotest/testoutputreader.cpp +++ b/src/plugins/autotest/testoutputreader.cpp @@ -6,7 +6,7 @@ #include "autotesttr.h" #include "testtreeitem.h" -#include +#include #include #include diff --git a/src/plugins/autotest/testrunner.cpp b/src/plugins/autotest/testrunner.cpp index 3631459fec4..29b7898835b 100644 --- a/src/plugins/autotest/testrunner.cpp +++ b/src/plugins/autotest/testrunner.cpp @@ -33,7 +33,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/plugins/autotoolsprojectmanager/autogenstep.cpp b/src/plugins/autotoolsprojectmanager/autogenstep.cpp index 020a9ad96ce..f1dde9c9211 100644 --- a/src/plugins/autotoolsprojectmanager/autogenstep.cpp +++ b/src/plugins/autotoolsprojectmanager/autogenstep.cpp @@ -14,7 +14,7 @@ #include #include -#include +#include #include diff --git a/src/plugins/autotoolsprojectmanager/autoreconfstep.cpp b/src/plugins/autotoolsprojectmanager/autoreconfstep.cpp index 810272ecad3..8ad689f1305 100644 --- a/src/plugins/autotoolsprojectmanager/autoreconfstep.cpp +++ b/src/plugins/autotoolsprojectmanager/autoreconfstep.cpp @@ -13,7 +13,7 @@ #include #include -#include +#include using namespace ProjectExplorer; using namespace Utils; diff --git a/src/plugins/autotoolsprojectmanager/configurestep.cpp b/src/plugins/autotoolsprojectmanager/configurestep.cpp index 97fdf3576e6..4303b298f72 100644 --- a/src/plugins/autotoolsprojectmanager/configurestep.cpp +++ b/src/plugins/autotoolsprojectmanager/configurestep.cpp @@ -13,7 +13,7 @@ #include #include -#include +#include #include diff --git a/src/plugins/autotoolsprojectmanager/makefileparser.cpp b/src/plugins/autotoolsprojectmanager/makefileparser.cpp index 8a2f684f877..d4468daa8a7 100644 --- a/src/plugins/autotoolsprojectmanager/makefileparser.cpp +++ b/src/plugins/autotoolsprojectmanager/makefileparser.cpp @@ -5,7 +5,7 @@ #include "autotoolsprojectmanagertr.h" -#include +#include #include #include diff --git a/src/plugins/baremetal/baremetaldebugsupport.cpp b/src/plugins/baremetal/baremetaldebugsupport.cpp index e3d32b3d5c2..521289650fd 100644 --- a/src/plugins/baremetal/baremetaldebugsupport.cpp +++ b/src/plugins/baremetal/baremetaldebugsupport.cpp @@ -24,7 +24,7 @@ #include #include -#include +#include #include using namespace Debugger; diff --git a/src/plugins/baremetal/debugservers/gdb/jlinkgdbserverprovider.cpp b/src/plugins/baremetal/debugservers/gdb/jlinkgdbserverprovider.cpp index 56495e32c6f..8d8a968c091 100644 --- a/src/plugins/baremetal/debugservers/gdb/jlinkgdbserverprovider.cpp +++ b/src/plugins/baremetal/debugservers/gdb/jlinkgdbserverprovider.cpp @@ -9,7 +9,7 @@ #include #include -#include +#include #include #include diff --git a/src/plugins/baremetal/debugservers/gdb/openocdgdbserverprovider.cpp b/src/plugins/baremetal/debugservers/gdb/openocdgdbserverprovider.cpp index ff4ae311afd..2ee3b266d76 100644 --- a/src/plugins/baremetal/debugservers/gdb/openocdgdbserverprovider.cpp +++ b/src/plugins/baremetal/debugservers/gdb/openocdgdbserverprovider.cpp @@ -9,7 +9,7 @@ #include #include -#include +#include #include #include diff --git a/src/plugins/baremetal/debugservers/uvsc/uvscserverprovider.h b/src/plugins/baremetal/debugservers/uvsc/uvscserverprovider.h index 2cce62f6c18..61bbe83f814 100644 --- a/src/plugins/baremetal/debugservers/uvsc/uvscserverprovider.h +++ b/src/plugins/baremetal/debugservers/uvsc/uvscserverprovider.h @@ -10,7 +10,7 @@ #include // for RunWorker -#include +#include namespace Utils { class PathChooser; } diff --git a/src/plugins/baremetal/iarewtoolchain.cpp b/src/plugins/baremetal/iarewtoolchain.cpp index 4fa7dfe2e50..ef804b0a8a2 100644 --- a/src/plugins/baremetal/iarewtoolchain.cpp +++ b/src/plugins/baremetal/iarewtoolchain.cpp @@ -16,7 +16,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/plugins/baremetal/keiltoolchain.cpp b/src/plugins/baremetal/keiltoolchain.cpp index 50ae98178d9..0ca7495c94c 100644 --- a/src/plugins/baremetal/keiltoolchain.cpp +++ b/src/plugins/baremetal/keiltoolchain.cpp @@ -16,7 +16,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/plugins/baremetal/sdcctoolchain.cpp b/src/plugins/baremetal/sdcctoolchain.cpp index 17130e861b4..971fd9f13a8 100644 --- a/src/plugins/baremetal/sdcctoolchain.cpp +++ b/src/plugins/baremetal/sdcctoolchain.cpp @@ -16,7 +16,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/plugins/beautifier/artisticstyle/artisticstyle.cpp b/src/plugins/beautifier/artisticstyle/artisticstyle.cpp index feb532d1471..2982ac267bc 100644 --- a/src/plugins/beautifier/artisticstyle/artisticstyle.cpp +++ b/src/plugins/beautifier/artisticstyle/artisticstyle.cpp @@ -29,7 +29,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/plugins/beautifier/beautifiertool.cpp b/src/plugins/beautifier/beautifiertool.cpp index 72f5a726209..cfcc117667f 100644 --- a/src/plugins/beautifier/beautifiertool.cpp +++ b/src/plugins/beautifier/beautifiertool.cpp @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/plugins/beautifier/uncrustify/uncrustify.cpp b/src/plugins/beautifier/uncrustify/uncrustify.cpp index e806b9026c4..75bd5252ed9 100644 --- a/src/plugins/beautifier/uncrustify/uncrustify.cpp +++ b/src/plugins/beautifier/uncrustify/uncrustify.cpp @@ -29,7 +29,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/plugins/boot2qt/device-detection/qdbwatcher.cpp b/src/plugins/boot2qt/device-detection/qdbwatcher.cpp index 869b2d84fcb..8a18a3b5ac9 100644 --- a/src/plugins/boot2qt/device-detection/qdbwatcher.cpp +++ b/src/plugins/boot2qt/device-detection/qdbwatcher.cpp @@ -8,7 +8,7 @@ #include "../qdbutils.h" #include -#include +#include #include #include diff --git a/src/plugins/boot2qt/qdbdevice.cpp b/src/plugins/boot2qt/qdbdevice.cpp index 9cc4852831f..1ede480417b 100644 --- a/src/plugins/boot2qt/qdbdevice.cpp +++ b/src/plugins/boot2qt/qdbdevice.cpp @@ -15,7 +15,7 @@ #include #include -#include +#include #include #include diff --git a/src/plugins/boot2qt/qdbdevicedebugsupport.cpp b/src/plugins/boot2qt/qdbdevicedebugsupport.cpp index 15373e4b4cf..87b885668e8 100644 --- a/src/plugins/boot2qt/qdbdevicedebugsupport.cpp +++ b/src/plugins/boot2qt/qdbdevicedebugsupport.cpp @@ -16,7 +16,7 @@ #include #include -#include +#include #include using namespace Debugger; diff --git a/src/plugins/boot2qt/qdbmakedefaultappstep.cpp b/src/plugins/boot2qt/qdbmakedefaultappstep.cpp index 3a777ddcb81..ca15acf7d47 100644 --- a/src/plugins/boot2qt/qdbmakedefaultappstep.cpp +++ b/src/plugins/boot2qt/qdbmakedefaultappstep.cpp @@ -14,7 +14,7 @@ #include #include -#include +#include using namespace ProjectExplorer; using namespace Tasking; diff --git a/src/plugins/boot2qt/qdbplugin.cpp b/src/plugins/boot2qt/qdbplugin.cpp index fbcd2667f6f..c0a5bb25ca8 100644 --- a/src/plugins/boot2qt/qdbplugin.cpp +++ b/src/plugins/boot2qt/qdbplugin.cpp @@ -29,7 +29,7 @@ #include #include -#include +#include using namespace Core; using namespace ProjectExplorer; diff --git a/src/plugins/boot2qt/qdbstopapplicationstep.cpp b/src/plugins/boot2qt/qdbstopapplicationstep.cpp index ce437993f34..341691c368d 100644 --- a/src/plugins/boot2qt/qdbstopapplicationstep.cpp +++ b/src/plugins/boot2qt/qdbstopapplicationstep.cpp @@ -13,7 +13,7 @@ #include -#include +#include using namespace ProjectExplorer; using namespace Tasking; diff --git a/src/plugins/clangtools/clangtoolrunner.cpp b/src/plugins/clangtools/clangtoolrunner.cpp index bd97eac2ea0..46fdf7c2730 100644 --- a/src/plugins/clangtools/clangtoolrunner.cpp +++ b/src/plugins/clangtools/clangtoolrunner.cpp @@ -16,7 +16,7 @@ #include #include -#include +#include #include #include diff --git a/src/plugins/clangtools/clangtoolsutils.cpp b/src/plugins/clangtools/clangtoolsutils.cpp index 029a879ce11..6a650e9c551 100644 --- a/src/plugins/clangtools/clangtoolsutils.cpp +++ b/src/plugins/clangtools/clangtoolsutils.cpp @@ -18,7 +18,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/plugins/clangtools/executableinfo.cpp b/src/plugins/clangtools/executableinfo.cpp index 22898b5734a..0b5d649cf50 100644 --- a/src/plugins/clangtools/executableinfo.cpp +++ b/src/plugins/clangtools/executableinfo.cpp @@ -7,7 +7,7 @@ #include #include -#include +#include #include #include diff --git a/src/plugins/clearcase/clearcaseplugin.cpp b/src/plugins/clearcase/clearcaseplugin.cpp index f566b8223d4..bc4cc82958a 100644 --- a/src/plugins/clearcase/clearcaseplugin.cpp +++ b/src/plugins/clearcase/clearcaseplugin.cpp @@ -35,7 +35,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/plugins/clearcase/clearcasesync.cpp b/src/plugins/clearcase/clearcasesync.cpp index 71682126a67..a92ab7da7bd 100644 --- a/src/plugins/clearcase/clearcasesync.cpp +++ b/src/plugins/clearcase/clearcasesync.cpp @@ -9,7 +9,7 @@ #include #include -#include +#include #include #include diff --git a/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp b/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp index f93f41e3e88..0ba54b60a81 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp +++ b/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp @@ -47,7 +47,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/plugins/cmakeprojectmanager/cmakeprocess.cpp b/src/plugins/cmakeprojectmanager/cmakeprocess.cpp index ef120914def..40cc41d64a5 100644 --- a/src/plugins/cmakeprojectmanager/cmakeprocess.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeprocess.cpp @@ -20,7 +20,7 @@ #include #include -#include +#include #include #include #include diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectimporter.cpp b/src/plugins/cmakeprojectmanager/cmakeprojectimporter.cpp index 52ed82f8d77..8bffb72ebc9 100644 --- a/src/plugins/cmakeprojectmanager/cmakeprojectimporter.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeprojectimporter.cpp @@ -22,7 +22,7 @@ #include #include -#include +#include #include #include #include diff --git a/src/plugins/cmakeprojectmanager/cmaketool.cpp b/src/plugins/cmakeprojectmanager/cmaketool.cpp index e90bc963a6f..6c06238dfdb 100644 --- a/src/plugins/cmakeprojectmanager/cmaketool.cpp +++ b/src/plugins/cmakeprojectmanager/cmaketool.cpp @@ -12,7 +12,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp b/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp index ad1e600124d..01c8b6cadb7 100644 --- a/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp +++ b/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp @@ -18,7 +18,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/plugins/coreplugin/dialogs/externaltoolconfig.cpp b/src/plugins/coreplugin/dialogs/externaltoolconfig.cpp index 1916a3ba259..dc434547de7 100644 --- a/src/plugins/coreplugin/dialogs/externaltoolconfig.cpp +++ b/src/plugins/coreplugin/dialogs/externaltoolconfig.cpp @@ -18,7 +18,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/plugins/coreplugin/externaltool.cpp b/src/plugins/coreplugin/externaltool.cpp index ea13e50c502..e0b60d4c550 100644 --- a/src/plugins/coreplugin/externaltool.cpp +++ b/src/plugins/coreplugin/externaltool.cpp @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/plugins/coreplugin/fileutils.cpp b/src/plugins/coreplugin/fileutils.cpp index e432543604f..5712c9c8b5b 100644 --- a/src/plugins/coreplugin/fileutils.cpp +++ b/src/plugins/coreplugin/fileutils.cpp @@ -17,7 +17,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/src/plugins/coreplugin/locator/executefilter.cpp b/src/plugins/coreplugin/locator/executefilter.cpp index a2cd1de91c0..629099207ed 100644 --- a/src/plugins/coreplugin/locator/executefilter.cpp +++ b/src/plugins/coreplugin/locator/executefilter.cpp @@ -11,7 +11,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/plugins/coreplugin/locator/spotlightlocatorfilter.cpp b/src/plugins/coreplugin/locator/spotlightlocatorfilter.cpp index 7ecd298a8c9..b360ad595db 100644 --- a/src/plugins/coreplugin/locator/spotlightlocatorfilter.cpp +++ b/src/plugins/coreplugin/locator/spotlightlocatorfilter.cpp @@ -16,7 +16,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/src/plugins/coreplugin/patchtool.cpp b/src/plugins/coreplugin/patchtool.cpp index c17db9ba927..fc463be49e3 100644 --- a/src/plugins/coreplugin/patchtool.cpp +++ b/src/plugins/coreplugin/patchtool.cpp @@ -9,7 +9,7 @@ #include "systemsettings.h" #include -#include +#include #include diff --git a/src/plugins/coreplugin/plugininstallwizard.cpp b/src/plugins/coreplugin/plugininstallwizard.cpp index 5b342bf6c6a..0ff62b13ff5 100644 --- a/src/plugins/coreplugin/plugininstallwizard.cpp +++ b/src/plugins/coreplugin/plugininstallwizard.cpp @@ -18,7 +18,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/src/plugins/coreplugin/progressmanager/processprogress.cpp b/src/plugins/coreplugin/progressmanager/processprogress.cpp index e9348fce41d..e3769f54ca9 100644 --- a/src/plugins/coreplugin/progressmanager/processprogress.cpp +++ b/src/plugins/coreplugin/progressmanager/processprogress.cpp @@ -6,7 +6,7 @@ #include "progressmanager.h" #include "../coreplugintr.h" -#include +#include #include #include diff --git a/src/plugins/cppcheck/cppcheckrunner.h b/src/plugins/cppcheck/cppcheckrunner.h index 686a6c9eb97..0ec49aceb54 100644 --- a/src/plugins/cppcheck/cppcheckrunner.h +++ b/src/plugins/cppcheck/cppcheckrunner.h @@ -4,7 +4,7 @@ #pragma once #include -#include +#include #include #include diff --git a/src/plugins/cppeditor/cppcodemodelsettings.cpp b/src/plugins/cppeditor/cppcodemodelsettings.cpp index 29516c62189..cf51d1d8d1c 100644 --- a/src/plugins/cppeditor/cppcodemodelsettings.cpp +++ b/src/plugins/cppeditor/cppcodemodelsettings.cpp @@ -15,7 +15,8 @@ #include #include -#include +#include +#include #include #include diff --git a/src/plugins/cppeditor/cppmodelmanager.cpp b/src/plugins/cppeditor/cppmodelmanager.cpp index 27aab615b71..bd5100b1b54 100644 --- a/src/plugins/cppeditor/cppmodelmanager.cpp +++ b/src/plugins/cppeditor/cppmodelmanager.cpp @@ -66,7 +66,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/src/plugins/debugger/cdb/cdbengine.cpp b/src/plugins/debugger/cdb/cdbengine.cpp index c7e20aae980..6f388d20dc1 100644 --- a/src/plugins/debugger/cdb/cdbengine.cpp +++ b/src/plugins/debugger/cdb/cdbengine.cpp @@ -43,7 +43,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/src/plugins/debugger/cdb/cdbengine.h b/src/plugins/debugger/cdb/cdbengine.h index b8afe1e2d91..e0909d5338d 100644 --- a/src/plugins/debugger/cdb/cdbengine.h +++ b/src/plugins/debugger/cdb/cdbengine.h @@ -10,7 +10,7 @@ #include -#include +#include #include diff --git a/src/plugins/debugger/dap/dapclient.cpp b/src/plugins/debugger/dap/dapclient.cpp index 327d62da7a8..00fe60da022 100644 --- a/src/plugins/debugger/dap/dapclient.cpp +++ b/src/plugins/debugger/dap/dapclient.cpp @@ -4,7 +4,7 @@ #include "dapclient.h" #include "qjsonarray.h" -#include +#include #include #include diff --git a/src/plugins/debugger/dap/dapclient.h b/src/plugins/debugger/dap/dapclient.h index 887b32eebb7..92acb0ddb1a 100644 --- a/src/plugins/debugger/dap/dapclient.h +++ b/src/plugins/debugger/dap/dapclient.h @@ -5,7 +5,7 @@ #include -#include +#include #include diff --git a/src/plugins/debugger/dap/dapengine.cpp b/src/plugins/debugger/dap/dapengine.cpp index 7f4f515e80c..d2f94b9feeb 100644 --- a/src/plugins/debugger/dap/dapengine.cpp +++ b/src/plugins/debugger/dap/dapengine.cpp @@ -31,7 +31,7 @@ #include #include -#include +#include #include #include diff --git a/src/plugins/debugger/dap/dapengine.h b/src/plugins/debugger/dap/dapengine.h index 19edd255982..b5b72eec07e 100644 --- a/src/plugins/debugger/dap/dapengine.h +++ b/src/plugins/debugger/dap/dapengine.h @@ -5,7 +5,7 @@ #include -#include +#include #include #include diff --git a/src/plugins/debugger/debuggerengine.cpp b/src/plugins/debugger/debuggerengine.cpp index b2224ac32b8..3c290c76dd8 100644 --- a/src/plugins/debugger/debuggerengine.cpp +++ b/src/plugins/debugger/debuggerengine.cpp @@ -54,7 +54,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/src/plugins/debugger/debuggeritem.cpp b/src/plugins/debugger/debuggeritem.cpp index 591b4bd38bc..c5b1bc7f6b6 100644 --- a/src/plugins/debugger/debuggeritem.cpp +++ b/src/plugins/debugger/debuggeritem.cpp @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/src/plugins/debugger/debuggeritemmanager.cpp b/src/plugins/debugger/debuggeritemmanager.cpp index b4fb2af2dea..b547ddab01f 100644 --- a/src/plugins/debugger/debuggeritemmanager.cpp +++ b/src/plugins/debugger/debuggeritemmanager.cpp @@ -26,7 +26,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/src/plugins/debugger/debuggerruncontrol.cpp b/src/plugins/debugger/debuggerruncontrol.cpp index 8ef76e7a75b..e26f5f85b8a 100644 --- a/src/plugins/debugger/debuggerruncontrol.cpp +++ b/src/plugins/debugger/debuggerruncontrol.cpp @@ -35,7 +35,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/src/plugins/debugger/debuggersourcepathmappingwidget.cpp b/src/plugins/debugger/debuggersourcepathmappingwidget.cpp index 1cb7c6402cd..be983cf584c 100644 --- a/src/plugins/debugger/debuggersourcepathmappingwidget.cpp +++ b/src/plugins/debugger/debuggersourcepathmappingwidget.cpp @@ -12,7 +12,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/plugins/debugger/gdb/gdbengine.cpp b/src/plugins/debugger/gdb/gdbengine.cpp index 919ee6c7534..73c4321dbce 100644 --- a/src/plugins/debugger/gdb/gdbengine.cpp +++ b/src/plugins/debugger/gdb/gdbengine.cpp @@ -36,7 +36,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/src/plugins/debugger/gdb/gdbengine.h b/src/plugins/debugger/gdb/gdbengine.h index c7efe0cbaf6..f002f8c33f9 100644 --- a/src/plugins/debugger/gdb/gdbengine.h +++ b/src/plugins/debugger/gdb/gdbengine.h @@ -15,7 +15,7 @@ #include #include -#include +#include #include #include diff --git a/src/plugins/debugger/lldb/lldbengine.cpp b/src/plugins/debugger/lldb/lldbengine.cpp index 1bb409bf948..171df7625f8 100644 --- a/src/plugins/debugger/lldb/lldbengine.cpp +++ b/src/plugins/debugger/lldb/lldbengine.cpp @@ -29,7 +29,7 @@ #include #include -#include +#include #include #include diff --git a/src/plugins/debugger/lldb/lldbengine.h b/src/plugins/debugger/lldb/lldbengine.h index 57cbd1e7c75..faf2cc3f6fb 100644 --- a/src/plugins/debugger/lldb/lldbengine.h +++ b/src/plugins/debugger/lldb/lldbengine.h @@ -11,7 +11,7 @@ #include #include -#include +#include #include #include diff --git a/src/plugins/debugger/moduleshandler.cpp b/src/plugins/debugger/moduleshandler.cpp index 9072d02463d..3d72f704576 100644 --- a/src/plugins/debugger/moduleshandler.cpp +++ b/src/plugins/debugger/moduleshandler.cpp @@ -11,7 +11,7 @@ #include #include -#include +#include #include #include diff --git a/src/plugins/debugger/pdb/pdbengine.cpp b/src/plugins/debugger/pdb/pdbengine.cpp index 96a06c7b810..e0bfa126f5a 100644 --- a/src/plugins/debugger/pdb/pdbengine.cpp +++ b/src/plugins/debugger/pdb/pdbengine.cpp @@ -21,7 +21,7 @@ #include #include -#include +#include #include #include diff --git a/src/plugins/debugger/pdb/pdbengine.h b/src/plugins/debugger/pdb/pdbengine.h index 1e365b36a7f..49dceb24417 100644 --- a/src/plugins/debugger/pdb/pdbengine.h +++ b/src/plugins/debugger/pdb/pdbengine.h @@ -4,7 +4,7 @@ #pragma once #include -#include +#include #include diff --git a/src/plugins/debugger/qml/qmlengine.cpp b/src/plugins/debugger/qml/qmlengine.cpp index 2ffe5bf313a..27624f61dd3 100644 --- a/src/plugins/debugger/qml/qmlengine.cpp +++ b/src/plugins/debugger/qml/qmlengine.cpp @@ -38,7 +38,7 @@ #include #include -#include +#include #include #include diff --git a/src/plugins/debugger/terminal.cpp b/src/plugins/debugger/terminal.cpp index 18092f132fe..e96e8b072d5 100644 --- a/src/plugins/debugger/terminal.cpp +++ b/src/plugins/debugger/terminal.cpp @@ -12,7 +12,7 @@ #include #include -#include +#include #include #include diff --git a/src/plugins/docker/dockerapi.cpp b/src/plugins/docker/dockerapi.cpp index 900e5bb18b2..537b6c66f38 100644 --- a/src/plugins/docker/dockerapi.cpp +++ b/src/plugins/docker/dockerapi.cpp @@ -7,7 +7,7 @@ #include #include -#include +#include #include #include diff --git a/src/plugins/docker/dockerdevice.cpp b/src/plugins/docker/dockerdevice.cpp index 68cdee29d03..69098b732db 100644 --- a/src/plugins/docker/dockerdevice.cpp +++ b/src/plugins/docker/dockerdevice.cpp @@ -46,7 +46,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/src/plugins/effectcomposer/effectcomposermodel.cpp b/src/plugins/effectcomposer/effectcomposermodel.cpp index 5f782c7cbe7..5a5ad5718c2 100644 --- a/src/plugins/effectcomposer/effectcomposermodel.cpp +++ b/src/plugins/effectcomposer/effectcomposermodel.cpp @@ -15,7 +15,7 @@ #include #include -#include +#include #include diff --git a/src/plugins/fakevim/fakevimplugin.cpp b/src/plugins/fakevim/fakevimplugin.cpp index 07b5ec3b740..7a7554da9ea 100644 --- a/src/plugins/fakevim/fakevimplugin.cpp +++ b/src/plugins/fakevim/fakevimplugin.cpp @@ -51,7 +51,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/plugins/fossil/fossilclient.cpp b/src/plugins/fossil/fossilclient.cpp index d24935595a6..662785930d4 100644 --- a/src/plugins/fossil/fossilclient.cpp +++ b/src/plugins/fossil/fossilclient.cpp @@ -16,7 +16,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/src/plugins/genericprojectmanager/genericproject.cpp b/src/plugins/genericprojectmanager/genericproject.cpp index b201b2e1fe1..17af30f9ea1 100644 --- a/src/plugins/genericprojectmanager/genericproject.cpp +++ b/src/plugins/genericprojectmanager/genericproject.cpp @@ -37,7 +37,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/plugins/git/branchmodel.cpp b/src/plugins/git/branchmodel.cpp index 99e6e010057..aca355f2cb3 100644 --- a/src/plugins/git/branchmodel.cpp +++ b/src/plugins/git/branchmodel.cpp @@ -10,7 +10,7 @@ #include #include -#include +#include #include #include diff --git a/src/plugins/git/branchview.cpp b/src/plugins/git/branchview.cpp index c99789a100a..555db9b01be 100644 --- a/src/plugins/git/branchview.cpp +++ b/src/plugins/git/branchview.cpp @@ -21,7 +21,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/src/plugins/git/changeselectiondialog.cpp b/src/plugins/git/changeselectiondialog.cpp index 12cc91eddb3..24daa938297 100644 --- a/src/plugins/git/changeselectiondialog.cpp +++ b/src/plugins/git/changeselectiondialog.cpp @@ -12,7 +12,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/plugins/git/gerrit/gerritmodel.cpp b/src/plugins/git/gerrit/gerritmodel.cpp index 8af786ac5e5..f0c3d0cadf5 100644 --- a/src/plugins/git/gerrit/gerritmodel.cpp +++ b/src/plugins/git/gerrit/gerritmodel.cpp @@ -10,7 +10,7 @@ #include #include -#include +#include #include #include diff --git a/src/plugins/git/gerrit/gerritplugin.cpp b/src/plugins/git/gerrit/gerritplugin.cpp index 3ef90358b9b..749084c20a9 100644 --- a/src/plugins/git/gerrit/gerritplugin.cpp +++ b/src/plugins/git/gerrit/gerritplugin.cpp @@ -22,7 +22,7 @@ #include #include -#include +#include #include #include diff --git a/src/plugins/git/gitclient.cpp b/src/plugins/git/gitclient.cpp index eb857c46859..296b790e2fe 100644 --- a/src/plugins/git/gitclient.cpp +++ b/src/plugins/git/gitclient.cpp @@ -27,7 +27,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/src/plugins/git/gitgrep.cpp b/src/plugins/git/gitgrep.cpp index 5675fe0f484..73feffd68f8 100644 --- a/src/plugins/git/gitgrep.cpp +++ b/src/plugins/git/gitgrep.cpp @@ -17,7 +17,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/plugins/git/mergetool.h b/src/plugins/git/mergetool.h index 727f9a85df0..1fd32e3a4fe 100644 --- a/src/plugins/git/mergetool.h +++ b/src/plugins/git/mergetool.h @@ -3,7 +3,7 @@ #pragma once -#include +#include #include #include diff --git a/src/plugins/gitlab/gitlabclonedialog.cpp b/src/plugins/gitlab/gitlabclonedialog.cpp index abd442de088..ae5390e941b 100644 --- a/src/plugins/gitlab/gitlabclonedialog.cpp +++ b/src/plugins/gitlab/gitlabclonedialog.cpp @@ -23,7 +23,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/plugins/gitlab/queryrunner.h b/src/plugins/gitlab/queryrunner.h index 08afc56e1b6..bdb5e06034a 100644 --- a/src/plugins/gitlab/queryrunner.h +++ b/src/plugins/gitlab/queryrunner.h @@ -4,7 +4,7 @@ #pragma once #include -#include +#include #include diff --git a/src/plugins/haskell/haskellmanager.cpp b/src/plugins/haskell/haskellmanager.cpp index b57b4177ffb..e62fa04bb23 100644 --- a/src/plugins/haskell/haskellmanager.cpp +++ b/src/plugins/haskell/haskellmanager.cpp @@ -13,7 +13,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/plugins/incredibuild/cmakecommandbuilder.cpp b/src/plugins/incredibuild/cmakecommandbuilder.cpp index 2f0dd751ae9..8732601cbc4 100644 --- a/src/plugins/incredibuild/cmakecommandbuilder.cpp +++ b/src/plugins/incredibuild/cmakecommandbuilder.cpp @@ -8,7 +8,7 @@ #include #include -#include +#include #include // Compile-time only diff --git a/src/plugins/ios/iosbuildstep.cpp b/src/plugins/ios/iosbuildstep.cpp index 8edfb3dbc2b..c9d396ddd46 100644 --- a/src/plugins/ios/iosbuildstep.cpp +++ b/src/plugins/ios/iosbuildstep.cpp @@ -20,7 +20,7 @@ #include #include -#include +#include #include #include diff --git a/src/plugins/ios/iosconfigurations.cpp b/src/plugins/ios/iosconfigurations.cpp index 8e748bfd6ef..b29a73c1f13 100644 --- a/src/plugins/ios/iosconfigurations.cpp +++ b/src/plugins/ios/iosconfigurations.cpp @@ -34,7 +34,7 @@ #include #include -#include +#include #include #include diff --git a/src/plugins/ios/iosdeploystep.cpp b/src/plugins/ios/iosdeploystep.cpp index 4d0117f627d..596dc84d6bb 100644 --- a/src/plugins/ios/iosdeploystep.cpp +++ b/src/plugins/ios/iosdeploystep.cpp @@ -21,7 +21,7 @@ #include -#include +#include #include #include diff --git a/src/plugins/ios/iosdevice.cpp b/src/plugins/ios/iosdevice.cpp index a99f9a338d7..e81c263672b 100644 --- a/src/plugins/ios/iosdevice.cpp +++ b/src/plugins/ios/iosdevice.cpp @@ -20,7 +20,7 @@ #include #include -#include +#include #include diff --git a/src/plugins/ios/iosdsymbuildstep.cpp b/src/plugins/ios/iosdsymbuildstep.cpp index 0c07ff4182d..c0aa4a06eea 100644 --- a/src/plugins/ios/iosdsymbuildstep.cpp +++ b/src/plugins/ios/iosdsymbuildstep.cpp @@ -22,7 +22,7 @@ #include #include -#include +#include #include #include diff --git a/src/plugins/ios/iosprobe.cpp b/src/plugins/ios/iosprobe.cpp index e934ea01f91..883263ae723 100644 --- a/src/plugins/ios/iosprobe.cpp +++ b/src/plugins/ios/iosprobe.cpp @@ -4,7 +4,7 @@ #include "iosprobe.h" #include -#include +#include #include #include diff --git a/src/plugins/ios/iosrunconfiguration.cpp b/src/plugins/ios/iosrunconfiguration.cpp index 6f5384cc9b6..676fb68724a 100644 --- a/src/plugins/ios/iosrunconfiguration.cpp +++ b/src/plugins/ios/iosrunconfiguration.cpp @@ -23,7 +23,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/plugins/ios/iosrunner.cpp b/src/plugins/ios/iosrunner.cpp index 02d47e914fc..9e2174c80bc 100644 --- a/src/plugins/ios/iosrunner.cpp +++ b/src/plugins/ios/iosrunner.cpp @@ -27,7 +27,7 @@ #include #include -#include +#include #include #include #include diff --git a/src/plugins/ios/iossimulator.cpp b/src/plugins/ios/iossimulator.cpp index e030fe911e0..ed9a5abf66a 100644 --- a/src/plugins/ios/iossimulator.cpp +++ b/src/plugins/ios/iossimulator.cpp @@ -8,7 +8,7 @@ #include #include -#include +#include #include diff --git a/src/plugins/ios/iostoolhandler.cpp b/src/plugins/ios/iostoolhandler.cpp index 811404b2da8..2f3fe4f288d 100644 --- a/src/plugins/ios/iostoolhandler.cpp +++ b/src/plugins/ios/iostoolhandler.cpp @@ -14,7 +14,7 @@ #include #include -#include +#include #include #include diff --git a/src/plugins/ios/simulatorcontrol.cpp b/src/plugins/ios/simulatorcontrol.cpp index f6968107609..4def4c0e471 100644 --- a/src/plugins/ios/simulatorcontrol.cpp +++ b/src/plugins/ios/simulatorcontrol.cpp @@ -7,7 +7,7 @@ #include #include -#include +#include #include #ifdef Q_OS_MAC diff --git a/src/plugins/languageclient/client.cpp b/src/plugins/languageclient/client.cpp index 7095c450481..12cd46c4016 100644 --- a/src/plugins/languageclient/client.cpp +++ b/src/plugins/languageclient/client.cpp @@ -54,7 +54,7 @@ #include #include -#include +#include #include #include diff --git a/src/plugins/languageclient/languageclientinterface.h b/src/plugins/languageclient/languageclientinterface.h index a6dee5e4eeb..1a9d56bfa94 100644 --- a/src/plugins/languageclient/languageclientinterface.h +++ b/src/plugins/languageclient/languageclientinterface.h @@ -8,7 +8,7 @@ #include #include -#include +#include #include #include diff --git a/src/plugins/languageclient/languageclientutils.cpp b/src/plugins/languageclient/languageclientutils.cpp index 8b9962b6c9b..0428fd6a844 100644 --- a/src/plugins/languageclient/languageclientutils.cpp +++ b/src/plugins/languageclient/languageclientutils.cpp @@ -22,7 +22,7 @@ #include #include -#include +#include #include #include #include diff --git a/src/plugins/mcusupport/mcuqmlprojectnode.h b/src/plugins/mcusupport/mcuqmlprojectnode.h index bf40a696f6b..2a06e5cde55 100644 --- a/src/plugins/mcusupport/mcuqmlprojectnode.h +++ b/src/plugins/mcusupport/mcuqmlprojectnode.h @@ -9,7 +9,7 @@ #include #include -#include +#include #include #include diff --git a/src/plugins/mcusupport/mcusupportversiondetection.cpp b/src/plugins/mcusupport/mcusupportversiondetection.cpp index 56fe961569c..79c12cc9b3f 100644 --- a/src/plugins/mcusupport/mcusupportversiondetection.cpp +++ b/src/plugins/mcusupport/mcusupportversiondetection.cpp @@ -4,7 +4,7 @@ #include "mcusupportversiondetection.h" #include "mcuhelpers.h" -#include +#include #include #include diff --git a/src/plugins/mercurial/mercurialclient.cpp b/src/plugins/mercurial/mercurialclient.cpp index fd568c9741f..6140706d001 100644 --- a/src/plugins/mercurial/mercurialclient.cpp +++ b/src/plugins/mercurial/mercurialclient.cpp @@ -12,7 +12,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/plugins/mesonprojectmanager/mesonbuildconfiguration.cpp b/src/plugins/mesonprojectmanager/mesonbuildconfiguration.cpp index 93ce63ad564..38d525502cd 100644 --- a/src/plugins/mesonprojectmanager/mesonbuildconfiguration.cpp +++ b/src/plugins/mesonprojectmanager/mesonbuildconfiguration.cpp @@ -28,7 +28,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/plugins/mesonprojectmanager/mesonwrapper.h b/src/plugins/mesonprojectmanager/mesonwrapper.h index 9b2e2bdbeb1..c38927fc9f2 100644 --- a/src/plugins/mesonprojectmanager/mesonwrapper.h +++ b/src/plugins/mesonprojectmanager/mesonwrapper.h @@ -9,7 +9,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/plugins/mesonprojectmanager/toolwrapper.cpp b/src/plugins/mesonprojectmanager/toolwrapper.cpp index 632465c219e..c0da501ec4b 100644 --- a/src/plugins/mesonprojectmanager/toolwrapper.cpp +++ b/src/plugins/mesonprojectmanager/toolwrapper.cpp @@ -3,7 +3,7 @@ #include "toolwrapper.h" -#include +#include #include diff --git a/src/plugins/nim/project/nimblebuildsystem.cpp b/src/plugins/nim/project/nimblebuildsystem.cpp index baed02b5254..91f4eb2f202 100644 --- a/src/plugins/nim/project/nimblebuildsystem.cpp +++ b/src/plugins/nim/project/nimblebuildsystem.cpp @@ -10,7 +10,7 @@ #include #include -#include +#include #include using namespace ProjectExplorer; diff --git a/src/plugins/nim/project/nimcompilerbuildstep.cpp b/src/plugins/nim/project/nimcompilerbuildstep.cpp index af730b4c854..aaf6ff42969 100644 --- a/src/plugins/nim/project/nimcompilerbuildstep.cpp +++ b/src/plugins/nim/project/nimcompilerbuildstep.cpp @@ -16,7 +16,7 @@ #include #include -#include +#include #include #include diff --git a/src/plugins/nim/project/nimtoolchain.cpp b/src/plugins/nim/project/nimtoolchain.cpp index 61362314d73..80036592644 100644 --- a/src/plugins/nim/project/nimtoolchain.cpp +++ b/src/plugins/nim/project/nimtoolchain.cpp @@ -16,7 +16,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/plugins/nim/suggest/server.h b/src/plugins/nim/suggest/server.h index 73e28096e29..15fd2b04ad0 100644 --- a/src/plugins/nim/suggest/server.h +++ b/src/plugins/nim/suggest/server.h @@ -3,7 +3,7 @@ #pragma once -#include +#include namespace Nim::Suggest { diff --git a/src/plugins/perforce/perforcechecker.h b/src/plugins/perforce/perforcechecker.h index d0f608de705..5f7d2b16739 100644 --- a/src/plugins/perforce/perforcechecker.h +++ b/src/plugins/perforce/perforcechecker.h @@ -4,7 +4,7 @@ #pragma once #include -#include +#include namespace Perforce::Internal { diff --git a/src/plugins/perforce/perforceplugin.cpp b/src/plugins/perforce/perforceplugin.cpp index 7e8dca82656..a9c14748ea9 100644 --- a/src/plugins/perforce/perforceplugin.cpp +++ b/src/plugins/perforce/perforceplugin.cpp @@ -29,7 +29,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/plugins/perfprofiler/perfprofilerruncontrol.cpp b/src/plugins/perfprofiler/perfprofilerruncontrol.cpp index 2d35f1790ee..e0fb0374267 100644 --- a/src/plugins/perfprofiler/perfprofilerruncontrol.cpp +++ b/src/plugins/perfprofiler/perfprofilerruncontrol.cpp @@ -18,7 +18,7 @@ #include #include -#include +#include #include #include diff --git a/src/plugins/perfprofiler/perfsettings.cpp b/src/plugins/perfprofiler/perfsettings.cpp index d5f3ac88ca6..5bd9e802e17 100644 --- a/src/plugins/perfprofiler/perfsettings.cpp +++ b/src/plugins/perfprofiler/perfsettings.cpp @@ -21,7 +21,7 @@ #include #include -#include +#include #include #include diff --git a/src/plugins/perfprofiler/perftracepointdialog.cpp b/src/plugins/perfprofiler/perftracepointdialog.cpp index d5cb2e16e3d..d726aa7d867 100644 --- a/src/plugins/perfprofiler/perftracepointdialog.cpp +++ b/src/plugins/perfprofiler/perftracepointdialog.cpp @@ -12,7 +12,7 @@ #include #include -#include +#include #include #include diff --git a/src/plugins/projectexplorer/abstractprocessstep.cpp b/src/plugins/projectexplorer/abstractprocessstep.cpp index 547599bc9d6..5cfb66e8114 100644 --- a/src/plugins/projectexplorer/abstractprocessstep.cpp +++ b/src/plugins/projectexplorer/abstractprocessstep.cpp @@ -9,7 +9,7 @@ #include "projectexplorertr.h" #include -#include +#include #include #include diff --git a/src/plugins/projectexplorer/customwizard/customwizardscriptgenerator.cpp b/src/plugins/projectexplorer/customwizard/customwizardscriptgenerator.cpp index fa1f455af4a..686c766838c 100644 --- a/src/plugins/projectexplorer/customwizard/customwizardscriptgenerator.cpp +++ b/src/plugins/projectexplorer/customwizard/customwizardscriptgenerator.cpp @@ -8,7 +8,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/plugins/projectexplorer/devicesupport/desktopdevice.cpp b/src/plugins/projectexplorer/devicesupport/desktopdevice.cpp index 08880aff3b7..2dae1a9b685 100644 --- a/src/plugins/projectexplorer/devicesupport/desktopdevice.cpp +++ b/src/plugins/projectexplorer/devicesupport/desktopdevice.cpp @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/src/plugins/projectexplorer/devicesupport/devicemanager.cpp b/src/plugins/projectexplorer/devicesupport/devicemanager.cpp index c8b95084de8..015b215fef5 100644 --- a/src/plugins/projectexplorer/devicesupport/devicemanager.cpp +++ b/src/plugins/projectexplorer/devicesupport/devicemanager.cpp @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/src/plugins/projectexplorer/devicesupport/deviceusedportsgatherer.cpp b/src/plugins/projectexplorer/devicesupport/deviceusedportsgatherer.cpp index 5312026f735..920d7f69c55 100644 --- a/src/plugins/projectexplorer/devicesupport/deviceusedportsgatherer.cpp +++ b/src/plugins/projectexplorer/devicesupport/deviceusedportsgatherer.cpp @@ -9,7 +9,7 @@ #include #include -#include +#include #include #include #include diff --git a/src/plugins/projectexplorer/devicesupport/sshparameters.cpp b/src/plugins/projectexplorer/devicesupport/sshparameters.cpp index 1467f958b99..2b2ebdabc00 100644 --- a/src/plugins/projectexplorer/devicesupport/sshparameters.cpp +++ b/src/plugins/projectexplorer/devicesupport/sshparameters.cpp @@ -8,7 +8,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/plugins/projectexplorer/extracompiler.cpp b/src/plugins/projectexplorer/extracompiler.cpp index 45d1c0e7344..0d281297339 100644 --- a/src/plugins/projectexplorer/extracompiler.cpp +++ b/src/plugins/projectexplorer/extracompiler.cpp @@ -18,7 +18,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/plugins/projectexplorer/gcctoolchain.cpp b/src/plugins/projectexplorer/gcctoolchain.cpp index 7daa47c0814..28792276a61 100644 --- a/src/plugins/projectexplorer/gcctoolchain.cpp +++ b/src/plugins/projectexplorer/gcctoolchain.cpp @@ -21,7 +21,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/plugins/projectexplorer/makestep.cpp b/src/plugins/projectexplorer/makestep.cpp index fe3796bb303..730215d81c8 100644 --- a/src/plugins/projectexplorer/makestep.cpp +++ b/src/plugins/projectexplorer/makestep.cpp @@ -19,7 +19,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/plugins/projectexplorer/msvctoolchain.cpp b/src/plugins/projectexplorer/msvctoolchain.cpp index 67d3f327814..65c4432febb 100644 --- a/src/plugins/projectexplorer/msvctoolchain.cpp +++ b/src/plugins/projectexplorer/msvctoolchain.cpp @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/src/plugins/projectexplorer/processparameters.cpp b/src/plugins/projectexplorer/processparameters.cpp index d9338ad03a2..a60f1059475 100644 --- a/src/plugins/projectexplorer/processparameters.cpp +++ b/src/plugins/projectexplorer/processparameters.cpp @@ -5,7 +5,7 @@ #include #include -#include +#include #include #include diff --git a/src/plugins/projectexplorer/projectmodels.cpp b/src/plugins/projectexplorer/projectmodels.cpp index dcbd3a89c89..d84df808eec 100644 --- a/src/plugins/projectexplorer/projectmodels.cpp +++ b/src/plugins/projectexplorer/projectmodels.cpp @@ -23,7 +23,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/src/plugins/projectexplorer/runconfigurationaspects.cpp b/src/plugins/projectexplorer/runconfigurationaspects.cpp index 96e4bc1e1f7..7b6c7f4f5d3 100644 --- a/src/plugins/projectexplorer/runconfigurationaspects.cpp +++ b/src/plugins/projectexplorer/runconfigurationaspects.cpp @@ -19,7 +19,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/plugins/projectexplorer/runcontrol.cpp b/src/plugins/projectexplorer/runcontrol.cpp index d2481f2ac63..37ed40f3c20 100644 --- a/src/plugins/projectexplorer/runcontrol.cpp +++ b/src/plugins/projectexplorer/runcontrol.cpp @@ -28,7 +28,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/src/plugins/projectexplorer/targetsetuppage.cpp b/src/plugins/projectexplorer/targetsetuppage.cpp index 72e77fd6644..5b752071fd5 100644 --- a/src/plugins/projectexplorer/targetsetuppage.cpp +++ b/src/plugins/projectexplorer/targetsetuppage.cpp @@ -20,7 +20,7 @@ #include #include -#include +#include #include #include diff --git a/src/plugins/projectexplorer/taskfile.cpp b/src/plugins/projectexplorer/taskfile.cpp index b6a95c0325a..1c95254e2ef 100644 --- a/src/plugins/projectexplorer/taskfile.cpp +++ b/src/plugins/projectexplorer/taskfile.cpp @@ -15,7 +15,7 @@ #include #include -#include +#include #include #include diff --git a/src/plugins/projectexplorer/toolchainconfigwidget.cpp b/src/plugins/projectexplorer/toolchainconfigwidget.cpp index c7ce623b50a..60b1479e7d8 100644 --- a/src/plugins/projectexplorer/toolchainconfigwidget.cpp +++ b/src/plugins/projectexplorer/toolchainconfigwidget.cpp @@ -7,7 +7,7 @@ #include "projectexplorertr.h" #include -#include +#include #include #include diff --git a/src/plugins/projectexplorer/userfileaccessor.cpp b/src/plugins/projectexplorer/userfileaccessor.cpp index 6a1059bcd1c..a370d80ff69 100644 --- a/src/plugins/projectexplorer/userfileaccessor.cpp +++ b/src/plugins/projectexplorer/userfileaccessor.cpp @@ -19,7 +19,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/plugins/python/pipsupport.cpp b/src/plugins/python/pipsupport.cpp index db58b3869c3..8723ca75109 100644 --- a/src/plugins/python/pipsupport.cpp +++ b/src/plugins/python/pipsupport.cpp @@ -16,7 +16,7 @@ #include #include #include -#include +#include using namespace Utils; diff --git a/src/plugins/python/pipsupport.h b/src/plugins/python/pipsupport.h index 7d7b9e135f1..da217282eb9 100644 --- a/src/plugins/python/pipsupport.h +++ b/src/plugins/python/pipsupport.h @@ -4,7 +4,7 @@ #pragma once #include -#include +#include #include #include diff --git a/src/plugins/python/pyside.cpp b/src/plugins/python/pyside.cpp index 5df87ff1bb7..79ffb8b9518 100644 --- a/src/plugins/python/pyside.cpp +++ b/src/plugins/python/pyside.cpp @@ -19,7 +19,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/plugins/python/pysideuicextracompiler.cpp b/src/plugins/python/pysideuicextracompiler.cpp index 45fb68066f9..c195aeaa1f7 100644 --- a/src/plugins/python/pysideuicextracompiler.cpp +++ b/src/plugins/python/pysideuicextracompiler.cpp @@ -3,7 +3,7 @@ #include "pysideuicextracompiler.h" -#include +#include using namespace ProjectExplorer; using namespace Utils; diff --git a/src/plugins/python/pythonbuildconfiguration.cpp b/src/plugins/python/pythonbuildconfiguration.cpp index 9e1192bcd80..d13e8cbc6ae 100644 --- a/src/plugins/python/pythonbuildconfiguration.cpp +++ b/src/plugins/python/pythonbuildconfiguration.cpp @@ -37,7 +37,7 @@ #include #include #include -#include +#include using namespace ProjectExplorer; using namespace Utils; diff --git a/src/plugins/python/pythonkitaspect.cpp b/src/plugins/python/pythonkitaspect.cpp index 254a720d7cc..0be12cead0a 100644 --- a/src/plugins/python/pythonkitaspect.cpp +++ b/src/plugins/python/pythonkitaspect.cpp @@ -12,7 +12,7 @@ #include #include -#include +#include #include diff --git a/src/plugins/python/pythonlanguageclient.cpp b/src/plugins/python/pythonlanguageclient.cpp index dbe6c434de6..86cebde0e94 100644 --- a/src/plugins/python/pythonlanguageclient.cpp +++ b/src/plugins/python/pythonlanguageclient.cpp @@ -32,7 +32,7 @@ #include #include -#include +#include #include #include diff --git a/src/plugins/python/pythonsettings.cpp b/src/plugins/python/pythonsettings.cpp index b144f4a7e84..2f2fcbaace1 100644 --- a/src/plugins/python/pythonsettings.cpp +++ b/src/plugins/python/pythonsettings.cpp @@ -31,7 +31,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/src/plugins/python/pythonutils.cpp b/src/plugins/python/pythonutils.cpp index 2ecfe495bcd..f012bdfd65d 100644 --- a/src/plugins/python/pythonutils.cpp +++ b/src/plugins/python/pythonutils.cpp @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include diff --git a/src/plugins/qbsprojectmanager/qbsbuildconfiguration.cpp b/src/plugins/qbsprojectmanager/qbsbuildconfiguration.cpp index 34fa9057450..6cf4cc7e31b 100644 --- a/src/plugins/qbsprojectmanager/qbsbuildconfiguration.cpp +++ b/src/plugins/qbsprojectmanager/qbsbuildconfiguration.cpp @@ -26,7 +26,7 @@ #include #include -#include +#include #include #include diff --git a/src/plugins/qbsprojectmanager/qbsprofilemanager.cpp b/src/plugins/qbsprojectmanager/qbsprofilemanager.cpp index cf9f663bc7e..3cfd68d5ce9 100644 --- a/src/plugins/qbsprojectmanager/qbsprofilemanager.cpp +++ b/src/plugins/qbsprojectmanager/qbsprofilemanager.cpp @@ -18,7 +18,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/plugins/qbsprojectmanager/qbssession.cpp b/src/plugins/qbsprojectmanager/qbssession.cpp index 4bbdbb6ebc7..b2801acfa26 100644 --- a/src/plugins/qbsprojectmanager/qbssession.cpp +++ b/src/plugins/qbsprojectmanager/qbssession.cpp @@ -15,7 +15,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/plugins/qbsprojectmanager/qbssettings.cpp b/src/plugins/qbsprojectmanager/qbssettings.cpp index 79299bbb32c..ea89d659855 100644 --- a/src/plugins/qbsprojectmanager/qbssettings.cpp +++ b/src/plugins/qbsprojectmanager/qbssettings.cpp @@ -11,7 +11,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/plugins/qmakeprojectmanager/librarydetailscontroller.cpp b/src/plugins/qmakeprojectmanager/librarydetailscontroller.cpp index 800d7350c19..c4652d2e352 100644 --- a/src/plugins/qmakeprojectmanager/librarydetailscontroller.cpp +++ b/src/plugins/qmakeprojectmanager/librarydetailscontroller.cpp @@ -13,7 +13,7 @@ #include #include -#include +#include #include #include diff --git a/src/plugins/qmakeprojectmanager/makefileparse.cpp b/src/plugins/qmakeprojectmanager/makefileparse.cpp index 9d2bda465f7..bfa504ed60a 100644 --- a/src/plugins/qmakeprojectmanager/makefileparse.cpp +++ b/src/plugins/qmakeprojectmanager/makefileparse.cpp @@ -5,7 +5,7 @@ #include -#include +#include #include #include diff --git a/src/plugins/qmakeprojectmanager/qmakebuildconfiguration.cpp b/src/plugins/qmakeprojectmanager/qmakebuildconfiguration.cpp index f3a71728d00..fb82f839d8f 100644 --- a/src/plugins/qmakeprojectmanager/qmakebuildconfiguration.cpp +++ b/src/plugins/qmakeprojectmanager/qmakebuildconfiguration.cpp @@ -36,7 +36,7 @@ #include #include -#include +#include #include #include diff --git a/src/plugins/qmakeprojectmanager/qmakemakestep.cpp b/src/plugins/qmakeprojectmanager/qmakemakestep.cpp index 901cf5653c6..ae8b370b778 100644 --- a/src/plugins/qmakeprojectmanager/qmakemakestep.cpp +++ b/src/plugins/qmakeprojectmanager/qmakemakestep.cpp @@ -22,7 +22,7 @@ #include #include -#include +#include #include #include diff --git a/src/plugins/qmakeprojectmanager/qmakeparsernodes.cpp b/src/plugins/qmakeprojectmanager/qmakeparsernodes.cpp index 9faa6e6c536..9c6ca5caa41 100644 --- a/src/plugins/qmakeprojectmanager/qmakeparsernodes.cpp +++ b/src/plugins/qmakeprojectmanager/qmakeparsernodes.cpp @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/src/plugins/qmakeprojectmanager/qmakeproject.cpp b/src/plugins/qmakeprojectmanager/qmakeproject.cpp index 6575366a44c..c118ba1920e 100644 --- a/src/plugins/qmakeprojectmanager/qmakeproject.cpp +++ b/src/plugins/qmakeprojectmanager/qmakeproject.cpp @@ -51,7 +51,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/plugins/qmakeprojectmanager/qmakeprojectimporter.cpp b/src/plugins/qmakeprojectmanager/qmakeprojectimporter.cpp index 20cebc3ac72..200de04db76 100644 --- a/src/plugins/qmakeprojectmanager/qmakeprojectimporter.cpp +++ b/src/plugins/qmakeprojectmanager/qmakeprojectimporter.cpp @@ -23,7 +23,7 @@ #include #include -#include +#include #include #include diff --git a/src/plugins/qmakeprojectmanager/qmakestep.cpp b/src/plugins/qmakeprojectmanager/qmakestep.cpp index 1d07e4e13ac..e012499b540 100644 --- a/src/plugins/qmakeprojectmanager/qmakestep.cpp +++ b/src/plugins/qmakeprojectmanager/qmakestep.cpp @@ -36,7 +36,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp index 9cd49177c8a..b9dbb67862b 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp @@ -60,7 +60,7 @@ #include #include -#include +#include #include #include diff --git a/src/plugins/qmldesigner/components/componentcore/resourcegenerator.cpp b/src/plugins/qmldesigner/components/componentcore/resourcegenerator.cpp index 7a48ed0652a..4a229564c64 100644 --- a/src/plugins/qmldesigner/components/componentcore/resourcegenerator.cpp +++ b/src/plugins/qmldesigner/components/componentcore/resourcegenerator.cpp @@ -16,8 +16,8 @@ #include -#include #include +#include #include #include diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp index 8988b7d09c9..33a100f7b20 100644 --- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp +++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp @@ -75,7 +75,7 @@ #include #include -#include +#include #include #include #include diff --git a/src/plugins/qmlpreview/qmlpreviewruncontrol.cpp b/src/plugins/qmlpreview/qmlpreviewruncontrol.cpp index 209bc06d1d3..17881d00bdc 100644 --- a/src/plugins/qmlpreview/qmlpreviewruncontrol.cpp +++ b/src/plugins/qmlpreview/qmlpreviewruncontrol.cpp @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/plugins/qmlprofiler/qmlprofilerruncontrol.cpp b/src/plugins/qmlprofiler/qmlprofilerruncontrol.cpp index af91963e023..3ab0647e810 100644 --- a/src/plugins/qmlprofiler/qmlprofilerruncontrol.cpp +++ b/src/plugins/qmlprofiler/qmlprofilerruncontrol.cpp @@ -20,7 +20,7 @@ #include -#include +#include #include #include diff --git a/src/plugins/qmlprojectmanager/qmlproject.cpp b/src/plugins/qmlprojectmanager/qmlproject.cpp index d2cac841a92..388169b3756 100644 --- a/src/plugins/qmlprojectmanager/qmlproject.cpp +++ b/src/plugins/qmlprojectmanager/qmlproject.cpp @@ -28,7 +28,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp b/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp index cd2910ec7fe..62abce5fe96 100644 --- a/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp +++ b/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp @@ -50,7 +50,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp b/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp index 77650c9dd81..5742b945a8d 100644 --- a/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp +++ b/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp @@ -36,7 +36,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/plugins/qnx/qnxanalyzesupport.cpp b/src/plugins/qnx/qnxanalyzesupport.cpp index fe87c15eafd..d21d0b44c02 100644 --- a/src/plugins/qnx/qnxanalyzesupport.cpp +++ b/src/plugins/qnx/qnxanalyzesupport.cpp @@ -9,7 +9,7 @@ #include -#include +#include #include diff --git a/src/plugins/qnx/qnxdebugsupport.cpp b/src/plugins/qnx/qnxdebugsupport.cpp index b2d923f2cbe..27c26e269c0 100644 --- a/src/plugins/qnx/qnxdebugsupport.cpp +++ b/src/plugins/qnx/qnxdebugsupport.cpp @@ -33,7 +33,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/plugins/qnx/qnxdeployqtlibrariesdialog.cpp b/src/plugins/qnx/qnxdeployqtlibrariesdialog.cpp index a134d25615f..8b3baadb20e 100644 --- a/src/plugins/qnx/qnxdeployqtlibrariesdialog.cpp +++ b/src/plugins/qnx/qnxdeployqtlibrariesdialog.cpp @@ -18,7 +18,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/plugins/qnx/qnxdevice.cpp b/src/plugins/qnx/qnxdevice.cpp index c810a3f2866..4d04d69b42c 100644 --- a/src/plugins/qnx/qnxdevice.cpp +++ b/src/plugins/qnx/qnxdevice.cpp @@ -20,7 +20,7 @@ #include #include -#include +#include #include #include diff --git a/src/plugins/qnx/qnxdevicetester.cpp b/src/plugins/qnx/qnxdevicetester.cpp index f4a94f0efc4..b2208d92c12 100644 --- a/src/plugins/qnx/qnxdevicetester.cpp +++ b/src/plugins/qnx/qnxdevicetester.cpp @@ -6,7 +6,7 @@ #include "qnxconstants.h" #include "qnxtr.h" -#include +#include using namespace Utils; diff --git a/src/plugins/qnx/qnxutils.cpp b/src/plugins/qnx/qnxutils.cpp index 634800fe993..6ab907b6e90 100644 --- a/src/plugins/qnx/qnxutils.cpp +++ b/src/plugins/qnx/qnxutils.cpp @@ -5,7 +5,7 @@ #include #include -#include +#include #include #include diff --git a/src/plugins/qnx/slog2inforunner.cpp b/src/plugins/qnx/slog2inforunner.cpp index 71264da1e7c..999b73b82fe 100644 --- a/src/plugins/qnx/slog2inforunner.cpp +++ b/src/plugins/qnx/slog2inforunner.cpp @@ -8,7 +8,7 @@ #include #include -#include +#include #include #include diff --git a/src/plugins/qtapplicationmanager/appmanagerinstallpackagestep.cpp b/src/plugins/qtapplicationmanager/appmanagerinstallpackagestep.cpp index f89514ba005..67c9f6bec6f 100644 --- a/src/plugins/qtapplicationmanager/appmanagerinstallpackagestep.cpp +++ b/src/plugins/qtapplicationmanager/appmanagerinstallpackagestep.cpp @@ -22,7 +22,7 @@ #include #include -#include +#include using namespace ProjectExplorer; using namespace Utils; diff --git a/src/plugins/qtapplicationmanager/appmanagerruncontrol.cpp b/src/plugins/qtapplicationmanager/appmanagerruncontrol.cpp index 5780b6043af..ba0ee370e73 100644 --- a/src/plugins/qtapplicationmanager/appmanagerruncontrol.cpp +++ b/src/plugins/qtapplicationmanager/appmanagerruncontrol.cpp @@ -30,7 +30,7 @@ #include #include -#include +#include #include using namespace ProjectExplorer; diff --git a/src/plugins/qtsupport/baseqtversion.cpp b/src/plugins/qtsupport/baseqtversion.cpp index 399efc3a1cd..ccc72baf6ec 100644 --- a/src/plugins/qtsupport/baseqtversion.cpp +++ b/src/plugins/qtsupport/baseqtversion.cpp @@ -34,7 +34,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/src/plugins/qtsupport/externaleditors.cpp b/src/plugins/qtsupport/externaleditors.cpp index 47e8d21e840..4009025b8dc 100644 --- a/src/plugins/qtsupport/externaleditors.cpp +++ b/src/plugins/qtsupport/externaleditors.cpp @@ -19,7 +19,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/plugins/qtsupport/qtsupportplugin.cpp b/src/plugins/qtsupport/qtsupportplugin.cpp index db0f8888af4..72c2ac4ae95 100644 --- a/src/plugins/qtsupport/qtsupportplugin.cpp +++ b/src/plugins/qtsupport/qtsupportplugin.cpp @@ -35,7 +35,7 @@ #include #include #include -#include +#include #include diff --git a/src/plugins/qtsupport/qtversionmanager.cpp b/src/plugins/qtsupport/qtversionmanager.cpp index 715eec8aa2d..3aa1397b309 100644 --- a/src/plugins/qtsupport/qtversionmanager.cpp +++ b/src/plugins/qtsupport/qtversionmanager.cpp @@ -21,7 +21,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/plugins/qtsupport/uicgenerator.cpp b/src/plugins/qtsupport/uicgenerator.cpp index 74b8355af9a..e79788f174e 100644 --- a/src/plugins/qtsupport/uicgenerator.cpp +++ b/src/plugins/qtsupport/uicgenerator.cpp @@ -11,7 +11,7 @@ #include #include -#include +#include #include #include diff --git a/src/plugins/remotelinux/customcommanddeploystep.cpp b/src/plugins/remotelinux/customcommanddeploystep.cpp index 211f48b4f47..97826fde5e9 100644 --- a/src/plugins/remotelinux/customcommanddeploystep.cpp +++ b/src/plugins/remotelinux/customcommanddeploystep.cpp @@ -11,7 +11,7 @@ #include #include -#include +#include using namespace ProjectExplorer; using namespace Tasking; diff --git a/src/plugins/remotelinux/filesystemaccess_test.cpp b/src/plugins/remotelinux/filesystemaccess_test.cpp index d23ac3f3a1b..0f2e5bd9379 100644 --- a/src/plugins/remotelinux/filesystemaccess_test.cpp +++ b/src/plugins/remotelinux/filesystemaccess_test.cpp @@ -11,7 +11,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/plugins/remotelinux/genericdeploystep.cpp b/src/plugins/remotelinux/genericdeploystep.cpp index 6071c4c8cae..3d1f0e5e3ab 100644 --- a/src/plugins/remotelinux/genericdeploystep.cpp +++ b/src/plugins/remotelinux/genericdeploystep.cpp @@ -19,7 +19,7 @@ #include #include -#include +#include #include using namespace ProjectExplorer; diff --git a/src/plugins/remotelinux/genericdirectuploadstep.cpp b/src/plugins/remotelinux/genericdirectuploadstep.cpp index 9817ce3f0af..383722873bc 100644 --- a/src/plugins/remotelinux/genericdirectuploadstep.cpp +++ b/src/plugins/remotelinux/genericdirectuploadstep.cpp @@ -15,7 +15,7 @@ #include #include -#include +#include #include #include diff --git a/src/plugins/remotelinux/linuxdevice.cpp b/src/plugins/remotelinux/linuxdevice.cpp index 30fac20a9d9..e29533a41b5 100644 --- a/src/plugins/remotelinux/linuxdevice.cpp +++ b/src/plugins/remotelinux/linuxdevice.cpp @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/src/plugins/remotelinux/linuxdevicetester.cpp b/src/plugins/remotelinux/linuxdevicetester.cpp index f279bc0c382..aad08703064 100644 --- a/src/plugins/remotelinux/linuxdevicetester.cpp +++ b/src/plugins/remotelinux/linuxdevicetester.cpp @@ -13,7 +13,7 @@ #include #include -#include +#include #include #include #include diff --git a/src/plugins/remotelinux/makeinstallstep.cpp b/src/plugins/remotelinux/makeinstallstep.cpp index 08e65e61261..38edbb6791e 100644 --- a/src/plugins/remotelinux/makeinstallstep.cpp +++ b/src/plugins/remotelinux/makeinstallstep.cpp @@ -21,7 +21,7 @@ #include #include -#include +#include #include #include diff --git a/src/plugins/remotelinux/publickeydeploymentdialog.cpp b/src/plugins/remotelinux/publickeydeploymentdialog.cpp index 6d0ead4ac6f..d3ade682cb2 100644 --- a/src/plugins/remotelinux/publickeydeploymentdialog.cpp +++ b/src/plugins/remotelinux/publickeydeploymentdialog.cpp @@ -11,7 +11,7 @@ #include #include -#include +#include #include #include diff --git a/src/plugins/remotelinux/remotelinuxsignaloperation.cpp b/src/plugins/remotelinux/remotelinuxsignaloperation.cpp index a4cd369ca4b..95e68a64854 100644 --- a/src/plugins/remotelinux/remotelinuxsignaloperation.cpp +++ b/src/plugins/remotelinux/remotelinuxsignaloperation.cpp @@ -7,7 +7,7 @@ #include #include -#include +#include #include using namespace ProjectExplorer; diff --git a/src/plugins/remotelinux/sshkeycreationdialog.cpp b/src/plugins/remotelinux/sshkeycreationdialog.cpp index ca00cca76af..03c6f7a543f 100644 --- a/src/plugins/remotelinux/sshkeycreationdialog.cpp +++ b/src/plugins/remotelinux/sshkeycreationdialog.cpp @@ -10,7 +10,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/plugins/remotelinux/tarpackagedeploystep.cpp b/src/plugins/remotelinux/tarpackagedeploystep.cpp index aa8fe3db309..5f79272f4a8 100644 --- a/src/plugins/remotelinux/tarpackagedeploystep.cpp +++ b/src/plugins/remotelinux/tarpackagedeploystep.cpp @@ -12,7 +12,7 @@ #include #include -#include +#include #include using namespace ProjectExplorer; diff --git a/src/plugins/screenrecorder/cropandtrim.cpp b/src/plugins/screenrecorder/cropandtrim.cpp index cdd63994a89..fe37fd5cf6e 100644 --- a/src/plugins/screenrecorder/cropandtrim.cpp +++ b/src/plugins/screenrecorder/cropandtrim.cpp @@ -9,7 +9,7 @@ #include #include -#include +#include #include #include #include diff --git a/src/plugins/screenrecorder/export.cpp b/src/plugins/screenrecorder/export.cpp index 0c9b67d2a34..15f38993adc 100644 --- a/src/plugins/screenrecorder/export.cpp +++ b/src/plugins/screenrecorder/export.cpp @@ -10,7 +10,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/plugins/screenrecorder/ffmpegutils.cpp b/src/plugins/screenrecorder/ffmpegutils.cpp index 96223c3781e..fa0a6f45ae7 100644 --- a/src/plugins/screenrecorder/ffmpegutils.cpp +++ b/src/plugins/screenrecorder/ffmpegutils.cpp @@ -12,7 +12,7 @@ #endif // WITH_TESTS #include -#include +#include #include #include diff --git a/src/plugins/screenrecorder/record.cpp b/src/plugins/screenrecorder/record.cpp index 7f14d3dc0d3..aba61ed2eba 100644 --- a/src/plugins/screenrecorder/record.cpp +++ b/src/plugins/screenrecorder/record.cpp @@ -11,7 +11,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/src/plugins/silversearcher/findinfilessilversearcher.cpp b/src/plugins/silversearcher/findinfilessilversearcher.cpp index a6f79ce6091..cb290e046d8 100644 --- a/src/plugins/silversearcher/findinfilessilversearcher.cpp +++ b/src/plugins/silversearcher/findinfilessilversearcher.cpp @@ -9,7 +9,7 @@ #include #include -#include +#include #include #include diff --git a/src/plugins/squish/objectsmapdocument.cpp b/src/plugins/squish/objectsmapdocument.cpp index 20c77d9ccee..ba5f70b0cd1 100644 --- a/src/plugins/squish/objectsmapdocument.cpp +++ b/src/plugins/squish/objectsmapdocument.cpp @@ -9,7 +9,7 @@ #include "squishtr.h" #include -#include +#include #include diff --git a/src/plugins/squish/squishprocessbase.h b/src/plugins/squish/squishprocessbase.h index 6582c95062b..d8af2e9d4f2 100644 --- a/src/plugins/squish/squishprocessbase.h +++ b/src/plugins/squish/squishprocessbase.h @@ -5,7 +5,7 @@ #include "squishconstants.h" -#include +#include #include diff --git a/src/plugins/squish/squishtools.h b/src/plugins/squish/squishtools.h index 223bfb7c23e..87b45d5af07 100644 --- a/src/plugins/squish/squishtools.h +++ b/src/plugins/squish/squishtools.h @@ -9,7 +9,7 @@ #include "suiteconf.h" #include -#include +#include #include #include diff --git a/src/plugins/subversion/subversionclient.cpp b/src/plugins/subversion/subversionclient.cpp index e30abd5bc81..e69809143d3 100644 --- a/src/plugins/subversion/subversionclient.cpp +++ b/src/plugins/subversion/subversionclient.cpp @@ -12,7 +12,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/plugins/terminal/shellintegration.h b/src/plugins/terminal/shellintegration.h index 4f1215fad94..905b088b389 100644 --- a/src/plugins/terminal/shellintegration.h +++ b/src/plugins/terminal/shellintegration.h @@ -5,7 +5,7 @@ #pragma once #include -#include +#include #include diff --git a/src/plugins/terminal/terminalwidget.h b/src/plugins/terminal/terminalwidget.h index acba039ba96..351ae457205 100644 --- a/src/plugins/terminal/terminalwidget.h +++ b/src/plugins/terminal/terminalwidget.h @@ -13,7 +13,7 @@ #include #include -#include +#include #include #include diff --git a/src/plugins/texteditor/basefilefind.cpp b/src/plugins/texteditor/basefilefind.cpp index 399e275fe4f..ce6d2f4999f 100644 --- a/src/plugins/texteditor/basefilefind.cpp +++ b/src/plugins/texteditor/basefilefind.cpp @@ -21,7 +21,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/plugins/texteditor/formattexteditor.cpp b/src/plugins/texteditor/formattexteditor.cpp index 14f3d4e4e78..8a0fac2cd4d 100644 --- a/src/plugins/texteditor/formattexteditor.cpp +++ b/src/plugins/texteditor/formattexteditor.cpp @@ -12,7 +12,7 @@ #include #include -#include +#include #include #include #include diff --git a/src/plugins/updateinfo/updateinfoplugin.cpp b/src/plugins/updateinfo/updateinfoplugin.cpp index a23d6c7cfc2..98e1160f3f6 100644 --- a/src/plugins/updateinfo/updateinfoplugin.cpp +++ b/src/plugins/updateinfo/updateinfoplugin.cpp @@ -16,7 +16,7 @@ #include #include -#include +#include #include #include diff --git a/src/plugins/valgrind/callgrindengine.cpp b/src/plugins/valgrind/callgrindengine.cpp index 06ff38f5abe..8422bb01769 100644 --- a/src/plugins/valgrind/callgrindengine.cpp +++ b/src/plugins/valgrind/callgrindengine.cpp @@ -10,7 +10,7 @@ #include #include -#include +#include #include #include diff --git a/src/plugins/valgrind/callgrindengine.h b/src/plugins/valgrind/callgrindengine.h index 2379e917374..c0e5ce2a8b6 100644 --- a/src/plugins/valgrind/callgrindengine.h +++ b/src/plugins/valgrind/callgrindengine.h @@ -8,7 +8,7 @@ #include "callgrind/callgrindparsedata.h" #include "callgrind/callgrindparser.h" -#include +#include #include namespace Valgrind::Internal { diff --git a/src/plugins/valgrind/callgrindtool.cpp b/src/plugins/valgrind/callgrindtool.cpp index 9718193279a..75d8dfac9d9 100644 --- a/src/plugins/valgrind/callgrindtool.cpp +++ b/src/plugins/valgrind/callgrindtool.cpp @@ -46,7 +46,7 @@ #include #include -#include +#include #include #include diff --git a/src/plugins/valgrind/memchecktool.cpp b/src/plugins/valgrind/memchecktool.cpp index 742d648caf1..8ae89a3193e 100644 --- a/src/plugins/valgrind/memchecktool.cpp +++ b/src/plugins/valgrind/memchecktool.cpp @@ -45,7 +45,7 @@ #include #include -#include +#include #include #include #include diff --git a/src/plugins/valgrind/valgrindmemcheckparsertest.cpp b/src/plugins/valgrind/valgrindmemcheckparsertest.cpp index 66824f718c3..44cac3712c6 100644 --- a/src/plugins/valgrind/valgrindmemcheckparsertest.cpp +++ b/src/plugins/valgrind/valgrindmemcheckparsertest.cpp @@ -11,7 +11,7 @@ #include "xmlprotocol/status.h" #include "xmlprotocol/suppression.h" -#include +#include #include #include diff --git a/src/plugins/valgrind/valgrindprocess.cpp b/src/plugins/valgrind/valgrindprocess.cpp index d543f3e66a3..b86eb0cf7a6 100644 --- a/src/plugins/valgrind/valgrindprocess.cpp +++ b/src/plugins/valgrind/valgrindprocess.cpp @@ -9,7 +9,7 @@ #include #include -#include +#include #include #include diff --git a/src/plugins/vcsbase/vcsbaseclient.cpp b/src/plugins/vcsbase/vcsbaseclient.cpp index 81a816c0099..2327e75bc7b 100644 --- a/src/plugins/vcsbase/vcsbaseclient.cpp +++ b/src/plugins/vcsbase/vcsbaseclient.cpp @@ -21,7 +21,7 @@ #include #include -#include +#include #include #include diff --git a/src/plugins/vcsbase/vcsbasediffeditorcontroller.cpp b/src/plugins/vcsbase/vcsbasediffeditorcontroller.cpp index cbd33f48fcf..4ba6baba34e 100644 --- a/src/plugins/vcsbase/vcsbasediffeditorcontroller.cpp +++ b/src/plugins/vcsbase/vcsbasediffeditorcontroller.cpp @@ -8,7 +8,7 @@ #include #include #include -#include +#include using namespace DiffEditor; using namespace Tasking; diff --git a/src/plugins/vcsbase/vcsbaseplugin.cpp b/src/plugins/vcsbase/vcsbaseplugin.cpp index b506f0ed380..a66596d49fe 100644 --- a/src/plugins/vcsbase/vcsbaseplugin.cpp +++ b/src/plugins/vcsbase/vcsbaseplugin.cpp @@ -17,7 +17,7 @@ #include #include -#include +#include #include #include diff --git a/src/plugins/vcsbase/vcsbasesubmiteditor.cpp b/src/plugins/vcsbase/vcsbasesubmiteditor.cpp index 4c29da91f2f..7a52cc96090 100644 --- a/src/plugins/vcsbase/vcsbasesubmiteditor.cpp +++ b/src/plugins/vcsbase/vcsbasesubmiteditor.cpp @@ -36,7 +36,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/src/plugins/vcsbase/vcscommand.cpp b/src/plugins/vcsbase/vcscommand.cpp index f1995b2d844..a10586458ec 100644 --- a/src/plugins/vcsbase/vcscommand.cpp +++ b/src/plugins/vcsbase/vcscommand.cpp @@ -10,7 +10,7 @@ #include #include -#include +#include #include #include diff --git a/src/plugins/vcsbase/vcsoutputwindow.cpp b/src/plugins/vcsbase/vcsoutputwindow.cpp index e8738907b56..747b409b5d1 100644 --- a/src/plugins/vcsbase/vcsoutputwindow.cpp +++ b/src/plugins/vcsbase/vcsoutputwindow.cpp @@ -14,7 +14,7 @@ #include #include -#include +#include #include #include diff --git a/src/plugins/webassembly/webassemblyemsdk.cpp b/src/plugins/webassembly/webassemblyemsdk.cpp index 12c4d649678..6541200eaa7 100644 --- a/src/plugins/webassembly/webassemblyemsdk.cpp +++ b/src/plugins/webassembly/webassemblyemsdk.cpp @@ -7,7 +7,7 @@ #include #include -#include +#include #include #include diff --git a/src/plugins/webassembly/webassemblyrunconfiguration.cpp b/src/plugins/webassembly/webassemblyrunconfiguration.cpp index e78222c0a6d..25ca3740343 100644 --- a/src/plugins/webassembly/webassemblyrunconfiguration.cpp +++ b/src/plugins/webassembly/webassemblyrunconfiguration.cpp @@ -18,7 +18,7 @@ #include #include -#include +#include #include #include diff --git a/tests/auto/utils/commandline/tst_commandline.cpp b/tests/auto/utils/commandline/tst_commandline.cpp index 1c7b7d4f807..d884e47c8d6 100644 --- a/tests/auto/utils/commandline/tst_commandline.cpp +++ b/tests/auto/utils/commandline/tst_commandline.cpp @@ -8,7 +8,7 @@ #include #include #include -#include +#include #include #include diff --git a/tests/auto/utils/deviceshell/tst_deviceshell.cpp b/tests/auto/utils/deviceshell/tst_deviceshell.cpp index 6e081495c36..f94d0dc5b40 100644 --- a/tests/auto/utils/deviceshell/tst_deviceshell.cpp +++ b/tests/auto/utils/deviceshell/tst_deviceshell.cpp @@ -8,7 +8,7 @@ #include #include #include -#include +#include #include #include diff --git a/tests/auto/utils/process/processtestapp/main.cpp b/tests/auto/utils/process/processtestapp/main.cpp index 34923c407aa..5b0dda04b32 100644 --- a/tests/auto/utils/process/processtestapp/main.cpp +++ b/tests/auto/utils/process/processtestapp/main.cpp @@ -6,7 +6,7 @@ #include #include -#include +#include #include #include diff --git a/tests/auto/utils/process/processtestapp/processtestapp.cpp b/tests/auto/utils/process/processtestapp/processtestapp.cpp index e080b580205..0148d1e2613 100644 --- a/tests/auto/utils/process/processtestapp/processtestapp.cpp +++ b/tests/auto/utils/process/processtestapp/processtestapp.cpp @@ -3,7 +3,7 @@ #include "processtestapp.h" -#include +#include #include #include diff --git a/tests/auto/utils/process/tst_process.cpp b/tests/auto/utils/process/tst_process.cpp index 60f3a72aa67..cd9f8715152 100644 --- a/tests/auto/utils/process/tst_process.cpp +++ b/tests/auto/utils/process/tst_process.cpp @@ -8,7 +8,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/tests/manual/deviceshell/tst_deviceshell.cpp b/tests/manual/deviceshell/tst_deviceshell.cpp index 99c506d1bda..96638763f8e 100644 --- a/tests/manual/deviceshell/tst_deviceshell.cpp +++ b/tests/manual/deviceshell/tst_deviceshell.cpp @@ -6,7 +6,7 @@ #include #include #include -#include +#include #include #include From b7b78df8be51b0479215e9161cd20cb96099ac74 Mon Sep 17 00:00:00 2001 From: hjk Date: Fri, 30 Jun 2023 07:32:46 +0200 Subject: [PATCH 153/176] Require C++20 Prevent introducing C++20 problems for the master merge. Change-Id: Id6581a4c32ec6411195edb51339f017e4ddb51ff Reviewed-by: Marco Bubke Reviewed-by: Eike Ziller (cherry picked from commit e678da9934acfdf3fe2dd4f2eaed8881f7cd35f3) Reviewed-by: Tim Jenssen --- CMakeLists.txt | 2 +- qbs/imports/QtcProduct.qbs | 2 +- share/qtcreator/cplusplus/examples/CMakeLists.txt | 2 +- src/libs/qtcreatorcdbext/CMakeLists.txt | 2 +- src/tools/qml2puppet/CMakeLists.txt | 2 +- src/tools/sdktool/CMakeLists.txt | 2 +- src/tools/wininterrupt/CMakeLists.txt | 2 +- tests/auto/debugger/CMakeLists.txt | 2 +- tests/manual/debugger/gui/CMakeLists.txt | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 63ca493c29a..e56479a98b9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,7 +30,7 @@ mark_as_advanced(IDE_REVISION IDE_REVISION_STR IDE_REVISION_URL) project(QtCreator VERSION ${IDE_VERSION}) # Force C++ standard, do not fall back, do not use compiler extensions -set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) diff --git a/qbs/imports/QtcProduct.qbs b/qbs/imports/QtcProduct.qbs index adf7314d0b2..428a23791df 100644 --- a/qbs/imports/QtcProduct.qbs +++ b/qbs/imports/QtcProduct.qbs @@ -47,7 +47,7 @@ Product { } return flags; } - cpp.cxxLanguageVersion: "c++17" + cpp.cxxLanguageVersion: "c++20" cpp.defines: qtc.generalDefines Properties { condition: sanitizable && qbs.toolchain.contains("gcc") diff --git a/share/qtcreator/cplusplus/examples/CMakeLists.txt b/share/qtcreator/cplusplus/examples/CMakeLists.txt index 004deeded3e..7d14eba5b98 100644 --- a/share/qtcreator/cplusplus/examples/CMakeLists.txt +++ b/share/qtcreator/cplusplus/examples/CMakeLists.txt @@ -8,7 +8,7 @@ set(CMAKE_AUTOUIC ON) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTORCC ON) -set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) find_package(QT NAMES Qt6 Qt5 COMPONENTS Core Gui Widgets REQUIRED) diff --git a/src/libs/qtcreatorcdbext/CMakeLists.txt b/src/libs/qtcreatorcdbext/CMakeLists.txt index 4359d79d546..47eca2cd163 100644 --- a/src/libs/qtcreatorcdbext/CMakeLists.txt +++ b/src/libs/qtcreatorcdbext/CMakeLists.txt @@ -4,7 +4,7 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake") project(qtcreatorcdbext) -set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) diff --git a/src/tools/qml2puppet/CMakeLists.txt b/src/tools/qml2puppet/CMakeLists.txt index b2f5edf2c91..b9f1a1544cc 100644 --- a/src/tools/qml2puppet/CMakeLists.txt +++ b/src/tools/qml2puppet/CMakeLists.txt @@ -13,7 +13,7 @@ if (NOT QT_CREATOR_API_DEFINED) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTORCC ON) set(CMAKE_AUTOUIC ON) - set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) diff --git a/src/tools/sdktool/CMakeLists.txt b/src/tools/sdktool/CMakeLists.txt index 19b8571f562..97c96909b61 100644 --- a/src/tools/sdktool/CMakeLists.txt +++ b/src/tools/sdktool/CMakeLists.txt @@ -7,7 +7,7 @@ project(sdktool) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTORCC ON) set(CMAKE_AUTOUIC ON) -set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) diff --git a/src/tools/wininterrupt/CMakeLists.txt b/src/tools/wininterrupt/CMakeLists.txt index ee40ff769b8..fdd5a33e4d1 100644 --- a/src/tools/wininterrupt/CMakeLists.txt +++ b/src/tools/wininterrupt/CMakeLists.txt @@ -4,7 +4,7 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake") project(wininterrupt) -set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) diff --git a/tests/auto/debugger/CMakeLists.txt b/tests/auto/debugger/CMakeLists.txt index 2f06a6b2fc7..01761ba5aa2 100644 --- a/tests/auto/debugger/CMakeLists.txt +++ b/tests/auto/debugger/CMakeLists.txt @@ -16,7 +16,7 @@ if (NOT QT_CREATOR_API_DEFINED) set(CMAKE_AUTORCC ON) set(CMAKE_AUTOUIC ON) - set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) diff --git a/tests/manual/debugger/gui/CMakeLists.txt b/tests/manual/debugger/gui/CMakeLists.txt index 412b0cdc429..1201ebd4f1c 100644 --- a/tests/manual/debugger/gui/CMakeLists.txt +++ b/tests/manual/debugger/gui/CMakeLists.txt @@ -5,7 +5,7 @@ project(manual_test_debugger_gui LANGUAGES CXX) set(CMAKE_AUTOUIC ON) set(CMAKE_AUTOMOC ON) -set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets) From 2f4ff01b7aa954d3c1fe80176230f24648b91d9f Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Mon, 11 Mar 2024 14:12:09 +0200 Subject: [PATCH 154/176] QmlDesigner: Add "Edit in 3D View" context menu option This option is only shown when View3D item is selected. Fixes: QDS-12207 Change-Id: I0a98b59a5eb3fe7abd689711939b649fe496eeb7 Reviewed-by: Mahmoud Badri --- .../componentcore/componentcore_constants.h | 2 ++ .../componentcore/designeractionmanager.cpp | 15 +++++++++++++-- .../componentcore/modelnodecontextmenu_helper.h | 6 ++++++ .../componentcore/modelnodeoperations.cpp | 9 +++++++++ .../componentcore/modelnodeoperations.h | 1 + 5 files changed, 31 insertions(+), 2 deletions(-) diff --git a/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h b/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h index 6a8833ef292..d992a6a5bf0 100644 --- a/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h +++ b/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h @@ -81,6 +81,7 @@ const char fitRootToScreenCommandId[] = "FitRootToScreen"; const char fitSelectionToScreenCommandId[] = "FitSelectionToScreen"; const char editAnnotationsCommandId[] = "EditAnnotation"; const char addMouseAreaFillCommandId[] = "AddMouseAreaFill"; +const char editIn3dViewCommandId[] = "editIn3dView"; const char openSignalDialogCommandId[] = "OpenSignalDialog"; const char update3DAssetCommandId[] = "Update3DAsset"; @@ -130,6 +131,7 @@ const char editMaterialDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu const char editCollectionDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Edit Model"); const char editAnnotationsDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Edit Annotations"); const char addMouseAreaFillDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Add Mouse Area"); +const char editIn3dViewDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Edit in 3D View"); const char openSignalDialogDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Open Signal Dialog"); const char update3DAssetDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Update 3D Asset"); diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp index 27afaa08f2b..4e32237ee95 100644 --- a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp +++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp @@ -1916,7 +1916,7 @@ void DesignerActionManager::createDefaultDesignerActions() contextIcon(DesignerIcons::EnterComponentIcon), rootCategory, QKeySequence(Qt::Key_F2), - Priorities::ComponentActions + 2, + Priorities::ComponentActions + 3, &goIntoComponentOperation, &selectionIsEditableComponent)); @@ -1963,13 +1963,24 @@ void DesignerActionManager::createDefaultDesignerActions() &singleSelectedAndUiFile)); } + addDesignerAction(new ModelNodeContextMenuAction( + editIn3dViewCommandId, + editIn3dViewDisplayName, + contextIcon(DesignerIcons::EditIcon), + rootCategory, + QKeySequence(), + Priorities::ComponentActions + 1, + &editIn3dView, + &singleSelectionView3D, + &singleSelectionView3D)); + addDesignerAction(new ModelNodeContextMenuAction( makeComponentCommandId, makeComponentDisplayName, contextIcon(DesignerIcons::MakeComponentIcon), rootCategory, QKeySequence(), - Priorities::ComponentActions + 1, + Priorities::ComponentActions + 2, &moveToComponent, &singleSelection, &singleSelection)); diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h b/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h index 593ace34d14..eb4915b1d0a 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h +++ b/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h @@ -97,6 +97,12 @@ inline bool singleSelectionNotRoot(const SelectionContext &selectionState) && !selectionState.currentSingleSelectedNode().isRootNode(); } +inline bool singleSelectionView3D(const SelectionContext &selectionState) +{ + return selectionState.singleNodeIsSelected() + && selectionState.currentSingleSelectedNode().metaInfo().isQtQuick3DView3D(); +} + inline bool selectionHasProperty(const SelectionContext &selectionState, const char *property) { for (const ModelNode &modelNode : selectionState.selectedModelNodes()) diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp index b9dbb67862b..cebe7d7c534 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp @@ -1689,6 +1689,15 @@ void updateImported3DAsset(const SelectionContext &selectionContext) } } +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); + } +} + bool isEffectComposerActivated() { const QVector specs = ExtensionSystem::PluginManager::plugins(); diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h index 4556462f30b..a67cef49424 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h @@ -124,6 +124,7 @@ void addMouseAreaFill(const SelectionContext &selectionContext); void openSignalDialog(const SelectionContext &selectionContext); void updateImported3DAsset(const SelectionContext &selectionContext); +void editIn3dView(const SelectionContext &selectionContext); QMLDESIGNERCOMPONENTS_EXPORT Utils::FilePath getEffectsImportDirectory(); QMLDESIGNERCOMPONENTS_EXPORT QString getEffectsDefaultDirectory(const QString &defaultDir = {}); From e82a6fc37c0cfff013c83f79b1d319552214e0f3 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Mon, 11 Mar 2024 13:39:25 +0100 Subject: [PATCH 155/176] QmlDesigner: Fix C++ 20 capture error Change-Id: Iba9b702878c3c04f1b10efd2b518f1a3054c5a55 Reviewed-by: Tim Jenssen --- .../qmldesigner/components/edit3d/edit3dview.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp index 763a8a38f1e..fafcff86671 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp @@ -438,10 +438,13 @@ void Edit3DView::customNotification([[maybe_unused]] const AbstractView *view, resetPuppet(); } else if (identifier == "pick_3d_node_from_2d_scene" && data.size() == 1 && nodeList.size() == 1) { // Pick via 2D view, data has pick coordinates in main scene coordinates - QTimer::singleShot(0, this, [=]() { - emitView3DAction(View3DActionType::GetNodeAtMainScenePos, - QVariantList{data[0], nodeList[0].internalId()}); - m_nodeAtPosReqType = NodeAtPosReqType::MainScenePick; + QTimer::singleShot(0, this, [=, self = QPointer{this}]() { + if (!self) + return; + + self->emitView3DAction(View3DActionType::GetNodeAtMainScenePos, + QVariantList{data[0], nodeList[0].internalId()}); + self->m_nodeAtPosReqType = NodeAtPosReqType::MainScenePick; }); } } From 914956eb5d6fb0776383675224ff3da4101b817c Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Fri, 8 Mar 2024 22:18:14 +0200 Subject: [PATCH 156/176] QmlDesigner: Add select all/none options to FlagsComboBox Fixes: QDS-12214 Change-Id: Iea02b76a1f2f57cfb9bb292dc26e9ce0a517693b Reviewed-by: Miikka Heikkinen --- .../imports/HelperWidgets/FlagsComboBox.qml | 55 ++++++++++++------- 1 file changed, 34 insertions(+), 21 deletions(-) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FlagsComboBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FlagsComboBox.qml index bb08fdcdff9..05a97e7a145 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FlagsComboBox.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FlagsComboBox.qml @@ -2,17 +2,14 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 import QtQuick -import StudioControls 1.0 as StudioControls -import StudioTheme 1.0 as StudioTheme +import StudioControls as StudioControls +import HelperWidgets as HelperWidgets +import StudioTheme as StudioTheme /* - A ComboBox of flags, it expects a list mode as below, first item will act as clear: + A ComboBox of flags, it expects a list model as below itemsModel: ListModel { - ListElement { - name: qsTr("Clear") - flag: 0 - } ListElement { name: "..." flag: ... @@ -41,11 +38,12 @@ StudioControls.CustomComboBox { let numSelected = 0 let selectedItem = "" - for (let i = 1; i < root.itemsModel.count; ++i) { - let flag = (root.backendValue.value >> (i - 1)) & 1; - root.popupItem.itemAt(i).checked = flag + for (let i = 0; i < root.itemsModel.count; ++i) { + let flag = root.itemsModel.get(i).flag + let flagActive = root.backendValue.value & flag + root.popupItem.itemAt(i).checked = flagActive - if (flag) { + if (flagActive) { selectedItem = root.itemsModel.get(i).name ++numSelected } @@ -70,6 +68,27 @@ StudioControls.CustomComboBox { return repeater.itemAt(idx) } + Row { + spacing: 5 + + HelperWidgets.Button { + text: qsTr("Select All") + + onClicked: { + let allFlags = 0 + for (let i = 0; i < root.itemsModel.count; ++i) + allFlags += root.itemsModel.get(i).flag + + root.backendValue.value = allFlags + } + } + HelperWidgets.Button { + text: qsTr("Select None") + + onClicked: root.backendValue.value = 0 + } + } + Repeater { id: repeater @@ -77,18 +96,12 @@ StudioControls.CustomComboBox { delegate: StudioControls.CheckBox { text: name actionIndicatorVisible: false - checked: (root.backendValue.value >> (index - 1)) & 1 onToggled: { - if (index === 0) { // Clear - root.popupItem.itemAt(0).checked = false - root.backendValue.value = 0 - } else { - if (root.popupItem.itemAt(index).checked) - root.backendValue.value |= flag - else - root.backendValue.value &= ~flag - } + if (root.popupItem.itemAt(index).checked) + root.backendValue.value |= flag + else + root.backendValue.value &= ~flag } } } From 09b00446b788063f1c821581cfa8a03800e71629 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Mon, 11 Mar 2024 15:04:49 +0100 Subject: [PATCH 157/176] Utils: Make ranges dependency more explicit Seems it does not work with MSVC and Mac OS Change-Id: I16fc72476021d88d02b0c0d2794ee726cde77272 Reviewed-by: Tim Jenssen --- src/libs/utils/ranges.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/utils/ranges.h b/src/libs/utils/ranges.h index 8b574e08d6f..5a3c3507c1c 100644 --- a/src/libs/utils/ranges.h +++ b/src/libs/utils/ranges.h @@ -3,8 +3,8 @@ #pragma once -#if __cplusplus >= 202002L -#include +#if __cpp_lib_ranges >= 202002L +# include namespace Utils { From 7bdf43e8e125831c164ec7ae3a9641ed90dba0c9 Mon Sep 17 00:00:00 2001 From: Shrief Gabr Date: Tue, 12 Mar 2024 01:46:26 +0200 Subject: [PATCH 158/176] EffectComposer: Hide effects from Components view Task-number: QDS-12147 Change-Id: I9df43dc41aebe7d526c2f75d120ec512f2c1172e Reviewed-by: Mahmoud Badri Reviewed-by: Miikka Heikkinen --- .../qmldesigner/components/itemlibrary/itemlibrarymodel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp index a0fec7e507a..dbeacc75954 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp @@ -323,7 +323,7 @@ void ItemLibraryModel::update([[maybe_unused]] ItemLibraryInfo *itemLibraryInfo, QHash importHash; for (const Import &import : model->imports()) { if (import.url() != projectName) { - if (excludedImports.contains(import.url())) + if (excludedImports.contains(import.url()) || import.url().startsWith("Effects.")) continue; bool addNew = true; bool isQuick3DAsset = import.url().startsWith("Quick3DAssets."); From 1e92dd4fe4d89558e00947045dae4a2e6f72d939 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Mon, 11 Mar 2024 15:11:18 +0200 Subject: [PATCH 159/176] QmlDesigner: Add tooltips as data type descriptors to Model Editor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task-number: QDS-11676 Change-Id: I14132ca8f2285de85532b26d3374b5e442f8c746 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri Reviewed-by: Miikka Heikkinen Reviewed-by: Henning Gründl --- .../CollectionDetailsToolbar.qml | 5 +- .../CollectionListView.qml | 2 +- .../EditPropertyDialog.qml | 5 +- .../imports/StudioControls/ComboBox.qml | 13 +++ src/plugins/qmldesigner/CMakeLists.txt | 1 + .../collectiondatatypemodel.cpp | 87 +++++++++++++++++++ .../collectiondatatypemodel.h | 34 ++++++++ .../collectioneditor/collectiondetails.cpp | 5 +- .../collectiondetailsmodel.cpp | 14 ++- .../collectioneditor/collectiondetailsmodel.h | 1 - .../collectioneditorutils.cpp | 76 ---------------- .../collectioneditor/collectioneditorutils.h | 6 -- .../collectioneditor/collectionview.cpp | 2 + 13 files changed, 154 insertions(+), 97 deletions(-) create mode 100644 src/plugins/qmldesigner/components/collectioneditor/collectiondatatypemodel.cpp create mode 100644 src/plugins/qmldesigner/components/collectioneditor/collectiondatatypemodel.h diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml index 0c8fcd5b989..7a5e77f42fb 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml @@ -8,6 +8,7 @@ import Qt.labs.platform as PlatformWidgets import HelperWidgets 2.0 as HelperWidgets import StudioControls 1.0 as StudioControls import StudioTheme 1.0 as StudioTheme +import CollectionDetails import CollectionEditorBackend Rectangle { @@ -231,7 +232,9 @@ Rectangle { Layout.fillWidth: true - model: root.model.typesList() + model: CollectionDataTypeModel{} + textRole: "display" + tooltipRole: "toolTip" actionIndicatorVisible: false } diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionListView.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionListView.qml index 1cde74fe375..ef06a2e7a04 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionListView.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionListView.qml @@ -11,7 +11,7 @@ ListView { clip: true delegate: CollectionItem { - implicitWidth: parent.width + implicitWidth: root.width onDeleteItem: root.model.removeRow(index) } } diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/EditPropertyDialog.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/EditPropertyDialog.qml index 814778f2b77..546dc5d770c 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/EditPropertyDialog.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/EditPropertyDialog.qml @@ -6,6 +6,7 @@ import QtQuick.Layouts import StudioTheme 1.0 as StudioTheme import StudioControls 1.0 as StudioControls import HelperWidgets 2.0 as HelperWidgets +import CollectionDetails StudioControls.Dialog { id: root @@ -91,7 +92,9 @@ StudioControls.Dialog { property string initialType - model: root.model.typesList() + model: CollectionDataTypeModel{} + textRole: "display" + tooltipRole: "toolTip" actionIndicatorVisible: false } } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ComboBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ComboBox.qml index 0c68d5195bd..006bb9037ca 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ComboBox.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ComboBox.qml @@ -36,6 +36,8 @@ T.ComboBox { property string preFocusText: "" + property string tooltipRole: "" + signal compressedActivated(int index, int reason) enum ActivatedReason { EditingFinished, Other } @@ -153,6 +155,17 @@ T.ComboBox { font: control.font elide: Text.ElideRight verticalAlignment: Text.AlignVCenter + + ToolTipArea { + anchors.fill: parent + text: control.tooltipRole ? (Array.isArray(control.model) + ? modelData[control.tooltipRole] + : model[control.tooltipRole]) + : "" + enabled: text + onClicked: itemDelegate.clicked() + onDoubleClicked: itemDelegate.doubleClicked() + } } highlighted: control.highlightedIndex === index diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 8412970d3b6..be7d5fa042e 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -836,6 +836,7 @@ extend_qtc_plugin(QmlDesigner extend_qtc_plugin(QmlDesigner SOURCES_PREFIX components/collectioneditor SOURCES + collectiondatatypemodel.cpp collectiondatatypemodel.h collectiondetails.cpp collectiondetails.h collectiondetailsmodel.cpp collectiondetailsmodel.h collectiondetailssortfiltermodel.cpp collectiondetailssortfiltermodel.h diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondatatypemodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondatatypemodel.cpp new file mode 100644 index 00000000000..b588f3be566 --- /dev/null +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondatatypemodel.cpp @@ -0,0 +1,87 @@ +// 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 "collectiondatatypemodel.h" + +#include +#include + +namespace QmlDesigner { + +struct CollectionDataTypeModel::Details +{ + CollectionDetails::DataType type; + QString name; + QString description; +}; + +const QList CollectionDataTypeModel::m_orderedDetails{ + {DataType::String, "String", "Text"}, + {DataType::Integer, "Integer", "Whole number"}, + {DataType::Real, "Real", "Number with a decimal"}, + {DataType::Image, "Image", "Image resource"}, + {DataType::Color, "Color", "HEX value"}, + {DataType::Url, "Url", "Resource locator"}, + {DataType::Boolean, "Boolean", "True/False"}, + {DataType::Unknown, "Unknown", "Unknown Data Type"}, +}; + +CollectionDataTypeModel::CollectionDataTypeModel(QObject *parent) + : QAbstractListModel(parent) +{ +} + +int CollectionDataTypeModel::rowCount([[maybe_unused]] const QModelIndex &parent) const +{ + return m_orderedDetails.size(); +} + +QVariant CollectionDataTypeModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return {}; + + if (role == Qt::DisplayRole) + return m_orderedDetails.at(index.row()).name; + if (role == Qt::ToolTipRole) + return m_orderedDetails.at(index.row()).description; + + return {}; +} + +QString CollectionDataTypeModel::dataTypeToString(DataType dataType) +{ + static const QHash dataTypeHash = []() -> QHash { + QHash result; + for (const Details &details : m_orderedDetails) + result.insert(details.type, details.name); + return result; + }(); + + if (dataTypeHash.contains(dataType)) + return dataTypeHash.value(dataType); + + return "Unknown"; +} + +CollectionDetails::DataType CollectionDataTypeModel::dataTypeFromString(const QString &dataType) +{ + static const QHash stringTypeHash = []() -> QHash { + QHash result; + for (const Details &details : m_orderedDetails) + result.insert(details.name, details.type); + return result; + }(); + + if (stringTypeHash.contains(dataType)) + return stringTypeHash.value(dataType); + + return DataType::Unknown; +} + +void CollectionDataTypeModel::registerDeclarativeType() +{ + qmlRegisterType("CollectionDetails", 1, 0, "CollectionDataTypeModel"); +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondatatypemodel.h b/src/plugins/qmldesigner/components/collectioneditor/collectiondatatypemodel.h new file mode 100644 index 00000000000..1f91aecbff7 --- /dev/null +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondatatypemodel.h @@ -0,0 +1,34 @@ +// 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 "collectiondetails.h" + +#include +#include + +namespace QmlDesigner { + +class CollectionDataTypeModel : public QAbstractListModel +{ + Q_OBJECT + +public: + using DataType = CollectionDetails::DataType; + CollectionDataTypeModel(QObject *parent = nullptr); + + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + + static Q_INVOKABLE QString dataTypeToString(DataType dataType); + static Q_INVOKABLE DataType dataTypeFromString(const QString &dataType); + + static void registerDeclarativeType(); + +private: + struct Details; + static const QList

    m_orderedDetails; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp index 8706a5dab0b..810923863bb 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp @@ -3,6 +3,7 @@ #include "collectiondetails.h" +#include "collectiondatatypemodel.h" #include "collectioneditorutils.h" #include @@ -706,7 +707,7 @@ QJsonObject CollectionDetails::toLocalJson() const for (const CollectionProperty &property : std::as_const(d->properties)) { QJsonObject columnObject; columnObject.insert("name", property.name); - columnObject.insert("type", CollectionEditorUtils::dataTypeToString(property.type)); + columnObject.insert("type", CollectionDataTypeModel::dataTypeToString(property.type)); columnsArray.append(columnObject); } @@ -941,7 +942,7 @@ CollectionDetails CollectionDetails::fromLocalCollection(const QJsonObject &loca result.insertColumn(columnName, -1, {}, - CollectionEditorUtils::dataTypeFromString( + CollectionDataTypeModel::dataTypeFromString( column.value("type").toString())); } } diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp index e7a246b68e7..326afb203ae 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp @@ -3,6 +3,7 @@ #include "collectiondetailsmodel.h" +#include "collectiondatatypemodel.h" #include "collectioneditorutils.h" #include "modelnode.h" @@ -188,7 +189,7 @@ QVariant CollectionDetailsModel::headerData(int section, Qt::Orientation orienta { if (orientation == Qt::Horizontal) { if (role == DataTypeRole) - return CollectionEditorUtils::dataTypeToString(m_currentCollection.typeAt(section)); + return CollectionDataTypeModel::dataTypeToString(m_currentCollection.typeAt(section)); else return m_currentCollection.propertyAt(section); } @@ -227,7 +228,7 @@ QString CollectionDetailsModel::propertyType(int column) const { QTC_ASSERT(m_currentCollection.hasValidReference(), return {}); - return CollectionEditorUtils::dataTypeToString(m_currentCollection.typeAt(column)); + return CollectionDataTypeModel::dataTypeToString(m_currentCollection.typeAt(column)); } bool CollectionDetailsModel::isPropertyAvailable(const QString &name) @@ -251,7 +252,7 @@ bool CollectionDetailsModel::addColumn(int column, const QString &name, const QS m_currentCollection.insertColumn(name, column, {}, - CollectionEditorUtils::dataTypeFromString(propertyType)); + CollectionDataTypeModel::dataTypeFromString(propertyType)); endInsertColumns(); return m_currentCollection.containsPropertyName(name); } @@ -298,7 +299,7 @@ bool CollectionDetailsModel::setPropertyType(int column, const QString &newValue QTC_ASSERT(m_currentCollection.hasValidReference(), return false); bool changed = m_currentCollection.setPropertyType(column, - CollectionEditorUtils::dataTypeFromString( + CollectionDataTypeModel::dataTypeFromString( newValue)); if (changed) { emit headerDataChanged(Qt::Horizontal, column, column); @@ -346,11 +347,6 @@ void CollectionDetailsModel::deselectAll() selectRow(-1); } -QStringList CollectionDetailsModel::typesList() -{ - return CollectionEditorUtils::dataTypesStringList(); -} - void CollectionDetailsModel::loadCollection(const ModelNode &sourceNode, const QString &collection) { QString fileName = CollectionEditorUtils::getSourceCollectionPath(sourceNode); diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h index 1fe152eaa24..0ad6e86f10c 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h @@ -58,7 +58,6 @@ public: Q_INVOKABLE bool selectRow(int row); Q_INVOKABLE void deselectAll(); Q_INVOKABLE QString warningToString(DataTypeWarning::Warning warning) const; - static Q_INVOKABLE QStringList typesList(); void loadCollection(const ModelNode &sourceNode, const QString &collection); void removeCollection(const ModelNode &sourceNode, const QString &collection); diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp index e1a17c51a15..4725987f12e 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp @@ -31,64 +31,6 @@ namespace { using CollectionDataVariant = std::variant; -class CollectionDataTypeHelper -{ -private: - using DataType = QmlDesigner::CollectionDetails::DataType; - friend QString QmlDesigner::CollectionEditorUtils::dataTypeToString(DataType); - friend DataType QmlDesigner::CollectionEditorUtils::dataTypeFromString(const QString &); - friend QStringList QmlDesigner::CollectionEditorUtils::dataTypesStringList(); - - CollectionDataTypeHelper() = delete; - - static QHash typeToStringHash() - { - return { - {DataType::Unknown, "Unknown"}, - {DataType::String, "String"}, - {DataType::Url, "Url"}, - {DataType::Real, "Real"}, - {DataType::Integer, "Integer"}, - {DataType::Boolean, "Boolean"}, - {DataType::Image, "Image"}, - {DataType::Color, "Color"}, - }; - } - - static QHash stringToTypeHash() - { - QHash stringTypeHash; - const QHash typeStringHash = typeToStringHash(); - for (const auto &transferItem : typeStringHash.asKeyValueRange()) - stringTypeHash.insert(transferItem.second, transferItem.first); - - return stringTypeHash; - } - - static QStringList orderedTypeNames() - { - const QList orderedtypes{ - DataType::String, - DataType::Integer, - DataType::Real, - DataType::Image, - DataType::Color, - DataType::Url, - DataType::Boolean, - DataType::Unknown, - }; - - QStringList orderedNames; - QHash typeStringHash = typeToStringHash(); - - for (const DataType &type : orderedtypes) - orderedNames.append(typeStringHash.take(type)); - - Q_ASSERT(typeStringHash.isEmpty()); - return orderedNames; - } -}; - inline bool operator<(const QColor &a, const QColor &b) { return a.name(QColor::HexArgb) < b.name(QColor::HexArgb); @@ -383,24 +325,6 @@ QJsonObject defaultColorCollection() return collection.toLocalJson(); } -QString dataTypeToString(CollectionDetails::DataType dataType) -{ - static const QHash typeStringHash = CollectionDataTypeHelper::typeToStringHash(); - return typeStringHash.value(dataType); -} - -CollectionDetails::DataType dataTypeFromString(const QString &dataType) -{ - static const QHash stringTypeHash = CollectionDataTypeHelper::stringToTypeHash(); - return stringTypeHash.value(dataType, DataType::Unknown); -} - -QStringList dataTypesStringList() -{ - static const QStringList typesList = CollectionDataTypeHelper::orderedTypeNames(); - return typesList; -} - bool writeToJsonDocument(const Utils::FilePath &path, const QJsonDocument &document, QString *errorString) { Core::FileChangeBlocker fileBlocker(path); diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h index 5be1c51f001..355addf59be 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h @@ -43,10 +43,4 @@ QJsonObject defaultCollection(); QJsonObject defaultColorCollection(); -QString dataTypeToString(CollectionDetails::DataType dataType); - -CollectionDetails::DataType dataTypeFromString(const QString &dataType); - -QStringList dataTypesStringList(); - } // namespace QmlDesigner::CollectionEditorUtils diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp index 93efc36a4fc..f6ec821fded 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp @@ -3,6 +3,7 @@ #include "collectionview.h" +#include "collectiondatatypemodel.h" #include "collectiondetailsmodel.h" #include "collectioneditorconstants.h" #include "collectioneditorutils.h" @@ -290,6 +291,7 @@ void CollectionView::openCollection(const QString &collectionName) void CollectionView::registerDeclarativeType() { CollectionDetails::registerDeclarativeType(); + CollectionDataTypeModel::registerDeclarativeType(); } void CollectionView::resetDataStoreNode() From eebd3472ae511e9d11167aebfbcd051a32136f42 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Tue, 12 Mar 2024 14:19:05 +0200 Subject: [PATCH 160/176] QmlDesigner: Close CustomComboBox's popup on view scroll Also remove the taskbar window (on Windows) that appears when the popup appears, and make the FlagsComboBox buttons smaller Fixes: QDS-12215 Change-Id: Ief8ab3b746a3d18b391b46a391cb53e775ecd624 Reviewed-by: Miikka Heikkinen Reviewed-by: Qt CI Patch Build Bot --- .../imports/HelperWidgets/FlagsComboBox.qml | 2 ++ .../imports/HelperWidgets/PropertyEditorPane.qml | 1 + .../imports/StudioControls/CustomComboBox.qml | 16 +++++++++++----- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FlagsComboBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FlagsComboBox.qml index 05a97e7a145..8bc9ad44282 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FlagsComboBox.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FlagsComboBox.qml @@ -73,6 +73,7 @@ StudioControls.CustomComboBox { HelperWidgets.Button { text: qsTr("Select All") + width: 80 onClicked: { let allFlags = 0 @@ -84,6 +85,7 @@ StudioControls.CustomComboBox { } HelperWidgets.Button { text: qsTr("Select None") + width: 80 onClicked: root.backendValue.value = 0 } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/PropertyEditorPane.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/PropertyEditorPane.qml index f35a21f4533..7046ca48e14 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/PropertyEditorPane.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/PropertyEditorPane.qml @@ -17,6 +17,7 @@ Rectangle { Component.onCompleted: Controller.mainScrollView = mainScrollView default property alias content: mainColumn.children + property alias scrollView: mainScrollView // Called from C++ to close context menu on focus out function closeContextMenu() { diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/CustomComboBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/CustomComboBox.qml index ba39201bdc8..88ebfaadb03 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/CustomComboBox.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/CustomComboBox.qml @@ -12,8 +12,6 @@ StudioControls.ComboBox { required property Component popupComponent - required property Item mainRoot - readonly property int popupHeight: Math.min(800, popupLoader.height + 2) property alias popupItem: popupLoader.item @@ -23,8 +21,8 @@ StudioControls.ComboBox { popup.height: 0 function calculateWindowGeometry() { - let gPos = globalPos(mainRoot.mapFromItem(root, 0, 0)) - let scrRect = screenRect(); + let gPos = globalPos(itemPane.mapFromItem(root, 0, 0)) + let scrRect = screenRect() window.width = Math.max(root.width - root.actionIndicator.width, popupLoader.width + 2) // 2: scrollView left and right 1px margins @@ -70,10 +68,18 @@ StudioControls.ComboBox { } } + Connections { + target: itemPane.scrollView + + function onContentYChanged() { + window.hide() // TODO: a better solution is to move the window instead of hiding + } + } + Window { id: window - flags: Qt.Dialog | Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint + flags: Qt.Tool | Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint onActiveFocusItemChanged: { if (!window.activeFocusItem && !root.hovered && root.popup.opened) From 7def8caf65e6e3117faa35f2c9af51b994058fce Mon Sep 17 00:00:00 2001 From: Teea Poldsam Date: Thu, 7 Mar 2024 14:56:26 +0200 Subject: [PATCH 161/176] Doc: Update Installation Rewrote almost the whole page, renamed the topic, and updated the image. Fixes: QDS-12025 Change-Id: Id8b4dcdb8513e726db933158833ba032ca9b8125 Reviewed-by: Mats Honkamaa --- .../external-resources-qds.qdoc | 8 +++ .../images/studio-installation.png | Bin 133863 -> 0 bytes .../images/studio-installation.webp | Bin 0 -> 15520 bytes .../src/qtdesignstudio-getting-started.qdoc | 2 +- .../src/qtdesignstudio-installation.qdoc | 67 ++++++++++-------- .../src/qtdesignstudio-toc.qdoc | 2 +- doc/qtdesignstudio/src/qtdesignstudio.qdoc | 2 +- 7 files changed, 48 insertions(+), 33 deletions(-) delete mode 100644 doc/qtdesignstudio/images/studio-installation.png create mode 100644 doc/qtdesignstudio/images/studio-installation.webp diff --git a/doc/qtcreator/src/external-resources/external-resources-qds.qdoc b/doc/qtcreator/src/external-resources/external-resources-qds.qdoc index ab6afa3fa56..d5b7fcf1bd2 100644 --- a/doc/qtcreator/src/external-resources/external-resources-qds.qdoc +++ b/doc/qtcreator/src/external-resources/external-resources-qds.qdoc @@ -61,3 +61,11 @@ \externalpage https://doc.qt.io/QtForMCUs/qtul-known-issues.html \title \QMCU Known Issues or Limitations */ +/*! + \externalpage https://doc.qt.io/qt/qt-edu-for-designers.html + \title Qt Edu for Designers +*/ +/*! + \externalpage https://www.qt.io/download + \title Try Qt +*/ diff --git a/doc/qtdesignstudio/images/studio-installation.png b/doc/qtdesignstudio/images/studio-installation.png deleted file mode 100644 index e3af24d70c19f73976c1c4e633bc70782efbc625..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 133863 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 diff --git a/doc/qtdesignstudio/images/studio-installation.webp b/doc/qtdesignstudio/images/studio-installation.webp new file mode 100644 index 0000000000000000000000000000000000000000..7b584a192572e63d2e37cb2a4137106ba3da9e11 GIT binary patch literal 15520 zcmWIYbaR_w!@v;k>J$(bVBynk!@!__rFpSHTH=#!xe_n`pU<0I!dKSc!O1XnS$uFc z>jxW`O-J{$O}%@2mfqR~R{nek*PkiYMv9xOI@As*`%RX7QXJbn^Mlv1{;YFxo!&Fs zb5l6O+mG}zXWTlv{DaaQe~W5^U%m*92L8`>(la>UJTe?I%)s`#WrRL_bM+C$M|>?%lTKyWeeJ-+$UVBs@H{ z{QS(@%m2xwEfU|vT;BW6PVV99JpHBY3r2Cjrfvybmot}X z?iJ^~EIjW+%IbLsB$gKWvugOVHR?8HI2M};VkLT&gBIveZTi;f+xRdpwi}&uL-^iOrLb@WG>x(xZdW*4`a!6apB{O z?lj)aD87BC`LNXbHK%Xb1{4*Zt!DmouW3U?er$lvcD-2@?_xSCPrU6_<0}y z*Q}Y#Z+D03o4{oKKUe=5x$I<)@BQg&y6m~gytoa4Cbhj!+#W~pvR&K2-NVFma`(>7 zNA5JWf68y{Iez=$@@zewEj<6miR%i>rLo^SOT=l(e! zO%H!_BIDGJqYgYvZ5ea|-Z|K>O}U+RWqahY5^?2=asozcb_;b*GFFNWZ=F*TYkF*6 zQ+(Zerh;p6x67F?1sJt&O;A|$l)d(9bEVs>I;Jlb%w8?k*_)jGs++S^`rb<|(5(8G zanLI)X0yhRA8KDdEnHI`Z6rVW#Mj)(N{1F-imNPLeKYs%%!NCD7P*?7k`6dzVjh2Y z)5~qie|%{h5Ml0EBb`}YrT?|BM7^?a6I=r5zCHCMIfcexSoSA&Z0 zy>CCUJu%Q?tCUt>^w-k#zm#v;f%&`LCOemZ_kDW*-mgxH;MuY^{}>aF?D~<*5PJCP zes51HC6~Tz;rr)wRSP|Sy!U_c*gNyIl;}0K;_g`w<Zp(>H2HbP(6@D-L zRMcv6E2?*D&TnbNkCO#c7Qep~Y?q51GE`}BtQ6F2WZ zVZiy0Z~dwrIewY9trWQ~x$kWI#&Q4MTAyI$UDEv559!rNt0$G%$8DCbsNOH9)yZ>oMG__*`!zNg`3wTm9{_)ZLwocQ3c_V+WK*I(?r@VcExmN)R)_OrXo zJUitV?EKL#FLh4$mdxgxUtN~Z`TyXrUH|tJzvOw&g!$waek$m*Fo;f`sHF@eXmF62}X2FNw&Q{!hukLYTd-$X2^NLQ{ zXT7U?@;UKvfBD6Ccc0t;ez$sQ?w2E1(w5)czqqn&!~2Ps1GSQQ{A~)aRXb06aI*i+ z)XXH64H`2Ko77p<6;H8@vesQGw7+xO$NpW3pQO(6UEjUpXN~FdpR-?FH#A?(Uz@u5X_6?_}M(Jlkmg&4bsv8PC0vt(P8=o4x*me@DsV5^!_W)3&qs$ zCzSc5xNo!7w_p9R+5Rm5{N+IZMnEmX*^PslL;f7m|g-<_R}LIMq8VOFwBehX`()p)(jd-R!xM`)vs;O`6_-S&eeA2(uKmE_u_kHPh?^#-TM7cSsY08S8CawuS zD?TKjo$EP?i=kXwXr<<40d6%94_5YbJDV8{Id?}pKYZ8oyHj>$xi8xcne0;y<_A9n zYaN|06T;Xt2UXqOgMT$%`F}?paRP=UmF7 zq`v-{#d3$8ReN)lH_CdmT?wq+!X&cY{cl2$!x`1ZRnyrm6;eFZ8CrrYcYB_2OpHoC zyGYU0`KZ?|-I=WUw$bk$4_Qx5?)&e&jLnVpr{VKYP3N}yEiyH5d-%X!Wy++96Pgbh zL|tr7I_dE2PRo6Ld&aOB;dE&JRTmd1TlM8Fo?J<5lV|T${AS5KcRTvD_|9VX8tLonzUj@?etjcg zf`LY?D_@B!+oTyMxq{qU=H2(qwfUUBYC(jMbZhmW2`XxTe(K)7<@MRW*GsdwHp+3` zq*ty%PjBxDytZ@YmdvTuPaIynoNLwa;rDbCO+kTa>sDR(^k2THc-0btZ~yGiuaEul zxO>W_tGjtyrkbsr5L6YeuexzUCfmff4;r@o`8`?d82f=JgNb+AQ*K8HwmppAueLN~ z(Qk$gJi4ZusV_eIa=R%psV~5!_Kz z)D`mBo=pL9Xk;^)3uGoqw z#qdk=&03}Gg$;q+Qj0#?{5m?LIPb(I^(?Vhn%Xzz=Ja{-zI(b~?Az31(|DHXFGi9sv3Z0XVZ^+>?#`mS8% zrGd}et}Iu76*E~N$Vt?c`+d)+YrASVk9ufscc18buJ)#(>E^nV409ZLW1D0h`f&JON_txIv z-XgtZ6X$0Bh6THq3O)aH=H`QwZ29c@r?mVtSie?IVOltq!%?(*8c)eQ#VfkHiy2rK z^(e~DF-&5r{CeAMhO*eoe%*5G4I1%AtJp&4re}D%>=g_6Kluu~%4~Cs#+6|`ipiC= z2G4^y)B1&Uf|$1Vdc>y|cW&p-KgFiMh`*=n+}47NtL5xFZ`7<*X~|%$6#U^fJD|s_ z+&D{XN6M0AJ)hYMoX(}sI?a;bz6v9AV8^V;m76<;<99KP6Cr4w(| zIO{COUNMO)`gcoT@2_@}oO&mYHUC>fL6hUsvpLNneFqowc+|W2i!GR*l69x}u^+Yuy5jXHHm(plisYmnE2gWny|8IEPYt;-RV-yyl}GFzE#{Ng->ojM*yy!n zlWd$~@w$~QNe^-scDCHUs3>?$vGSZuAk)Q^iFVDeE@|j(+Pgj_V7>pl#C-=jUA9J? zw$zQtULM3NVbi5tQyNlXWUngelz-};&<6Jax7jtNkquYa|0dU-|99E4`an3-GP`|A zFK66m&+>n@ImpMQr>l=sd^3kw3=gN)98TS-FMCuXwcoh8r=*3pGS9iax|t#R)_Fml z?3I^;e@CvJbNl?lBYUremNZoU;)%=8+}|y%mB4hN%D|wjdSRI8g{()aPG?T9;#z)n zSJKA^-U;_s6}cOHRC44#s9EbK5G>5@|7TaJg&_ClwNr{B{%;A-)fC%3H%i6r=yt~Q z4394H);{>PyCyZylEduyk{adzGdeV$iWT_Xwf8PJxq0_!yVB0tR-5~G{hP)8EbDo@X~JkC#_A9g|ZhuQff)*lMH+y8y_{&Q@*wH=RN4ZnH+ zj?=HIHcDT&H|4qWu5NDsrf*(v+PK@LW6#z1r8F*0=8JP(_-vy44QZv6b_HGCXvyWp zYqxb5r(fZp@!VjaBGdPSIa*8rEqPd=e_oqG#eYJ4X!S^8_wOJ2c>i$`y}ZShS0c}(a@ukwr8K_PzbZ^HkV zU$sx29l`Of_vLBc6Xtyn^1T9g&Rtu7{pO0BRp*yYFz=g??-l47yY}9}tgqf|Uk<(D z72M>l{0nTzjoCpPzKd{36+iEM)VW)_GjRQv%6ggTT)Bu{tIvOEps z33XR`U3cxMj+?9d>dxsLrTa_gZc{0EWW_oq&B{OG!J6o8(^VrLJUd&Gx1;0Gql@Y1 zo}XK-{azr)?#HE;Y46-3_E$#~-uP~?{XpfNXL;spa`P8$sF?EZ*gLna7kmtUK8{)J zB*~t5?AMlu@`m!QM;EV&+I)X0=jX1~d}S=J;tlvVw!Q3h4SfD~{jH!i*`d#0l&(F@ zH77yLLenea&VkRhy9^4d-4?OSNKKp79+#H2IJQ{2+tsvFIH9R{-i8C7j)@zY>X)eV z+=*l@Dti<5$m?b3xw=UQ{{MBi`?J5^>srl%^70=(dy;0nUza8Ihe7H(RbUyTmT>#{E-{7#$&|?M`ZFcxOr^3}licrans%c*l>6Drr{6Yl$Te9z zg;sQkq-Fh%E0$g=qw{!&;npj<*`1~rw%hVM6*63CbG>juo_W=Gp~DBAtT(RxsXkYF zT{der6MMOjMd2gWW1GY_Y_rsP!?CAFOrkck{eEQ=V_=b@hMsXGWW8mh_!%X^Xe4j=a8VvA~9Hnssbz)x)=M;8FSdDl_HT z2D!k1Wqn!C7P0B}%=)xt^9#*6@}GNVNZLuI8*r*Q9@0`*`ub|3s=fD(7a?WaU)LQ? z%exS~1!uQ6tEsI1S zG046w*`L*IA)mmpcTrW2R+_;&GyhXDq3^b6z0Yvvt(xSO%Xd5COHXxz{o5U9-EPk+ z&b(AD9Mkx^>|H`eYg5NXU(H9M(I@^EE@ga@5f#XKD&!)M`3ucCnli2n)$A`${rF&V z&T6rQD~}%U-Sp$j$1BRqf_21-|4sfpKPJNG?hT$LxigwuuFsWyQ}$i=)3W|`W}fOx zCqCSLb>F)+3X-$y_vNN;W#`DByyD>%|1i%d-TQ7Pv$dKS|2d>TYvs@7zuMLxFcj1+ zKdZ1@X8J4%g$QHGsb2S7D=bY@g^~`sOj%)b#B6T=c zvFZOQvZgve?kLTaQIbd~TC-CvE_>1Z03A2|`?`NWEbCu0t@3C3jf(e-eS(wcY;jcN z<()abzO5lz<@%Adr-^<>&#PaG{5dK9GUSi!f~|1|fmQ-n7>Y__3cFPTUvn;HnZi70 zmP#kn(^p^reQn(+TfA?&)vEZAzO(NXJPs^rnBnb~Wjle{$?qB`!@`hrcD|tY|Kt3> zH`G^4YK2|O>wI}&#e?(nXQ|Z31^jL5bjePLVPXG2xi!d0s3<_fO}i@K%+I)wax<39 z|Gd{ke5Iw(T1C+XE0R>3JgXMIG+V)7AgecHiht8n9SalAI|~y8Bko=LnDPE)!bElj zYn6qPkIZXNs`&m$zp^-)&Eu^6vZra+Z9_lk9r_sH{4vC^QRobZCD+nt1~=OS|2Dl6 zsWXaty-V=ql$tXy6W{ZzXsf*x6O}(O<)>4ftMIm2x`y7jHysXj*zV~W6kw^%d9^@z z_Lm3q1JAp97Hxk0YVzmF`4d&H^UOXubA{u{hus`+#CCS*7H!UWGU*qq=;s4BnI7IT z*D<}FG)e5k#lVM)*o3(zn>?KVaKWS>D$@UtrQSZ|b$~fmXj7xo+8^m|ZtbfX-b^y< zY=3JumE(Abv-Lba9e3w=9d(elY59pX9WJBU9k8 z!FEA$md>J>JpM%++m~Iu`gNUYo9yv-jy!y-GVvS3@^)~1J+F_6(P9C0;G-;0ZWxc|Ly`*J6%!ktd4S0=fjBnLTV`2qbLHnut=^AxA^--ai(k4&nwZaN&!6^U}V{#h&c{p6Hj&x|{lKJUxUSrB|V=;m&< z%qbjm57;SsCU`q(yt=yQ!v7$v8r7t~#*^nfSSZVEX7GYP#(2k3$%{cc33=Qmzu0ZA z?Y<__U+OezPx2x~6OBIGaMokCCd^xxzCNLA8+Lu#lZ^%rcGI}>)Hwu=7WO(VjXRn= z_rZ6Kq`$__%t1Hj@a>3RySQa~^hviJ^XzAsY|rfFWee!K%Ml}z@J)rSzc6mPl%GWz z|DNfm6B zN%z-Ibrn<+WZeAs;1bDmZ>)J1h&WE2arTZH$FrQmTh$;(r{{VGG-jUK9x3f8e(TgY&v)IX*6uv+bHmA)ssC)nnUq{LH@9%q#wm&%+PHqT>zewsm<#nsG+3CvC(K?Vt9nr9 z%0pqbvjv^^OdOYRWENcQ+o8K)&h4vP+tWTscrs-^628LtnPnwoUys788~1vyi``}9+>|BC&*S^NGNzdliI z6`>L{XDXO|dS2<_4+V!e$C=gsOiyJJm@S>Vs!dJdZS}D~GBKT-`3(n-?&axEvVw{Pc51Iq=RTdP*9r%f^R zOh5ZTS?`?3hB@jE)7c%JrrU3_&310Rsr=*l&de2$Qp=SCl~yfYX|I`h^Ht;8!%nJM zU&H**lr#joO)%s-_vnCqWvH{Lez>m0G_L}wtoBp4)Ppjd)_(hXgvGH|=8ci zmnBS4`eVu?6ZPNc_>a3UX1rwDA+#**OUUcj*{A!}gxyp%82ok~=iYjCuh%;hf3x2H zfW`Y4mM+knBM=lI{;Fm2uh_{-CLx!Cb;Vb{V_h!k;41yQb#->|PLJj-cV4pzE^buz zmRywbq(H#$KaaFzs+*8~-zzIdfu`bn#q;EacSe^PEavcNzjJ;i&*#8vDO@#PLc*5< z&Q7W1=JimpKd@EUn?v9Hsa0CO&mOH$e?C7AlX^QZSU>sJj^~%}H%Er4`Mq72+|>P( zp;N7eLxL;*$Irz5<|hpjOjy?`tO{V`au$Bn!j|>+_L@5@*T4AW|ESY#_wy^KXPCYE z`Q>|pqPB9|{8Wc3I~J1!1?S5RHkEr)+wa`$HH!FjBZsTzLP1I1gf#YpriXZTqy_D3 zwaaM!V^DGW*0AtbAxE-j+|+kINQLeG`wuLDh6=lRl|Ai6W8zJZ&z%UUByg#M9oc3w>kU)6{H^ zUT}ORj|CgOc zgr5;_jA+^7ILS{u$tp&4vZJ79VpW*e{;>E>72_@=S-9AVfQ_?fB(NetN$f= z7iB)RN&%d5uY?G0o*_Y+)TuL9-xMS`6<>Fpd6;`b zXI{>QnSmAa3QGjTC%uXZkv+HbTZfXsznRZwZrV4o^MVHf|WsZ$F!V@_PfjGJYf{i6YyIy^Qfvxz1Q>t!LzRKjJG+n1ZbW;{`ap=FYjNK z2jSuQDl6tp58|J@{=p{~HosoQ`ZA;D)7R85?|30Gi9KeAqs7D&G4Tn1mbgqXjIWew zR#gA?QvN)@!#s8uxruXQt#`XD%V#_NFjhlkjY88FjhWpxxl?vb%58pnq^;&!jnnIm z2NljIIW2iM-GBBZ-^ZK2-tuf)bMsQ?=3N5aosqFTGrMh;H9kEeX1PAw&{Jtk_wg(tIVZ47se~!izi34vQYIrylPdsk?MZC$5nf`EE6H&vowC2I5O-QR_G_$mYwLp2!nLLhJk45J9>C}a7axu?ZH2=8HjCVa}FErN8^AP3;whcLY zr}AiMwpv_@PPXBV^C=CMeg>N>A6flblhT=&wr8{7iM%y2{XMEVlNRkUn8T^saU%b! zpZD7>s{$rW@XBUXDXLzk|G($Y)iB)|>?PLh%XVt4zLqo3`h1T4WX5L}tvt!zuCsbS zXZ8kmor=j7c=$PWN5+A5EoaT1-&*J)5R{{yF!kxKQ;VD&U0zL-y=2>D@?`$g)<0Ns0`kOv)(xVujS^Q-(bz`5mi0SkW#iotgiNc4Xo1^b)P3bTa)!~*g z6@THmWK$HwK^Lpf=QGmvRT@OBmYn{|KI06dVcv~Hb<2KCNbP>3%)e~@)HO|h8_p+- znT9O_nG#YGp)t|5TB^&?czUqIlCDY2sxOb*pJp%0KDx7jb*)U)+_r~x>#{}GXm5}5 z-Q%)h!+cHqlt_y}h2`HKEuJ}b*To9;zcy(P?P^SH=T#m4ny_S6#G+$+ZvSOfYDqo% zY~~u#I~HcQFCCTiO?k{`S*DsJcI!^-EBioDFi_~g7<%H?A@cNWUtoMz#5#OG(h!5Y!17NKO``aQCqGeaJ0)c#Co z`%$4~9CX&WSE49kR#5|2^)Z`9gB%mR9A`FzTxPdoVpikP7R_jL z4;>MoV;dv;3a0k{c6E$j@xK4|v<*9x>_mh;rfirTQ`Gh6uVa&>TIhi+&MC|O7z?*Z zt<<|RY0i~PY-Z=Xa!;hhYDn}X2TR|)RaKYa`BK4Ymj2GemzLD0C9xP>7w@+@ajDQK zA|UDv&m5kdH~L3%TlS<(I#p}v1sd-@|Ijfo^)Q3A`K9ZAhiVEg+gvo<`uI`it$A_3 z+4Gh%#B&v!dfr+2_H(i6k2(P%=bIZ0d5nK_WCkn$N-UQCeU`BzD}?Q+gX2m=>(ssbux$VzXvqT}xLx3s@|eA@z3N zDZ_cM-x;1iXE1&5h5NhCHS6jxHEVwGsORgG*P$!&?k_lOI&ELqQ`f`<`@fGhV>>6Y zr3QTI3X0meA&mDL|A~N9g)8sBKAllj6?la)SiO^rN%cXF&I3{J9@f190c#ena}*O6 zGp%`KrCo6(iBWv&$_w$bN}-F%c;dXG?t2P zceyu(IlhB4n??0&b*<&HxsSM<#HBwL#qI96r>EL^-Sk9PP@#icSC>`R@s9#$yK`Fv z?%cl2ebmUyrLk{OjC*lm|B@|X@^O7DkMl*H)4rU&CVPTvtn7Bl>`9z#=B}zn`e}hK z+zFr34!cx{ntXqiAYeULl4E)Op8GprOR%wJ36{UHQm{(ev!UkIloyY`{$|x&d-e2b z!@uoqqNV<{r+$M>)AFt$=XrF5l`#2RGP9GQh8<n z+=C`;w!U^GB6z{qx@T|tblt^fZr=Up>`mTz5pU~i&fer*2_oj-SgrGO)@Es|k1;p* zJ$uvFr3Mt$K1y(&+q@v&Rxo9fA?4vchtl=vcFuDB(}YgFbRlQcwc#+^ZrdGp$jiONZ492 z)rnp2(JGDBZCBSm+Gep*GWge_HS*_Uwk}Z(UbN}){Fj3B-@cJGlz4y4xR5LLB!7q* z-_hA0eVSH8s!1_^>X%qz8Q*O4DaB@T0fij<(%WO$sc{V zFP(9D*?sNX@~I{%Z^h3Z_+!jqGt2Z|Px>rDGYe(@zF7$ZOZY>~`qJJN&Q1>g^Y4C$ zkb8gEQRT}Df4?TTm@jbKak*G+x|W9FO_xh2HoWcsxAn```7XRO&pGSYzuk0Hblb9- z5AFYM{qpsK_mK%NZRY)Q-@a__!~SLKj|VHa`KT+O-1g=0HZLEyN$#f?bO+v=)zQm$GJb@SP^B+25t+V>;zMi#mPZB1pNF09ud*glkBQB2reNJ?AH2td+P^-%kn)C2@ z+YF5t^Eo92A$s0(3-H%$@a*Vp`ZsgJMnxyHmQEE9=Z?;%zbeW~Xa4?G=&AVNe>dt= zU1b=Hz_Y(9iGmf-d|0|)eu-euKgo+fF4+rLG+g-py?H|7 z^ZDBupL6%uT#wpz|F^`8#7_6~T;>b!`!C{s|8~pKo-8ibwnaymPWa;R;`eXI6CWPT z@BDCLvzUF`2ZR23qIpjD?S&T!%RlNcaX<0wIx6TXS&$nc>OI}?QeJ$z7o)pjSM7!i&3Sa*IRY+MK z`AgkmzK@4ZRoISj?%Mx89bayKUJ-aURrkr~Nvgihoy+Pju`GQ5o?UUbecsFYr+z+a z3DZ~l_#*P*K6|-e|7HrjRZd&gJ^QYT{7N0Er{2OW3-`+}nRq<-;lHRaw)3P|e|we~ zM)uAzzj7|8rCuMGq)0rI`3)*UxQ;$uBj?>R%HMa;75BguYsrvtp zn~P_&ryQ_Vm@_p>ZFY6-le7nRMmv1{6r*ZXEkMGa!+W}H5+c8$!9lfu{9R2V~V@ET~bi$U2UoU$-T?t3JpRIBu|!9GVlzo z+Ou)}%dm}HTb}uM>uqE^!ahsadQRSg=%3L>T&p%syZCI6LU#FE+2G5wQY<>|u5jLv zw2#{~x@^ak!^*s3pJZLRJ{7x|GQXI#q0b}u(~Np=UX9}lhnF+&ym2#Zd)k!6FB{Gt zZ{vyld*4+1S@*f=Vn1av?4I3`UvYimZC!)l?O~@cHPy%;+4w)&{=utAInEzBIcGI3 z7V*zIaBZ39jZ+`)EiuV`H}TnS$NAqE`LsJPlzuaj_uWJf=_e2J4sph2&s!=Wx2FE4 z{**Z6#28fwyf|SL)6oa@jF<;YrUQ_ zX-(@YpUbEq^4FL#gr_NB8~2C3K3!8*{oMEUkmlhlXJd3%Ot-$mCei6LbALkn>O;)k zybm+aZgxoE`Tl(8uB~Doj?wdj=3KYmUCbl$kYS}mjO+y=ory`D{x2_VT&e!yuW{p! zWqTX9*zjsCs-7w085*3p@~P|-$+=wjUq=R3KQC|ou;2?eLas;^VwPLiANG#|E`l=x7RWEq_AS<+06z@{IBPK z(=_bVd&H>wiOI6j>*v&(?FZO2!#=%vz_)an@l%$)^=bxzN&k~}Gu%Dh;_>*LN^7c` zLrTEahiuy`W^u%3&tuIy#Aor&e(95@?86J^A7S3c(Y|6!18bCyd-he91S65Z#*If_ zt()ug-N-^AV#T#XMp9CDUj5&EH3muKyhN%ASAcvghEs z7d!0@EVoX7F4#GbXAZmal|-q`@}+YEzMOAyYF~0HX-jJR5vQ#p(>88MWzVnEzvOBs zJxAb@+_q~r+$Y-RP0CqOy!zSYS>{W&x1`?@w@KI>{dezjx#-nN{EWw({a@z%xF%nD zb#2^k#)G?`1T7cvPS=h-I74<)-ExJ=P2zfKC-(Xzh57DEaysU;@bJT>o3x5{91zf7 zzp8D$>G`IQ7fx65wX+udc)q%GVQ|;wl_gV7=+C(Q@#+z0{Y$Ec3OD2=gmb(8_sJ0W zbc;uJZQz?!7VfJ_=4TQz7Oj5HC11d!mcldtZ)unl4fAHn2Sp+wSJ^&cN^3 zj+cImZB^s!%jXJCd3t%r%WrEe8zoL;R){=%+~;XvyFP5CT6fqZuXFW$_73fppk=);G+zHY#a9i?*5d%5?df-Xl8-Qi304bajfR&-Rj;deKQ*`&`Hqne6SF z2GcZ!gtl&&zCojDTJ@})F1bzYx0lO#^8TJt#{V*Dw#|n9FF#JOubcJna9)wn#XH49 zvyIcw)&3VSWVcH>@9%ZYc6V4^NB&&n=Zf_eK6M=mcZ#Pt*B%LZv_e$n`<~jyjd>F` zUs}cy#dk_#L(^5T~J#e3U5&MlshF6Sb-c7}w-?|FA` zZ>eFCQIv5zdVh2F_e?XxkLPVxY-6!oFj>S$vT#M6nQC9qp6}ARt2z#MEqJ=3gF&+9 zySeST9NSLMOH$|FIoevS@bWBz2?d6Ahc^OXh!_c}~UVv-7u# zJAY;_|9<18{n5batRT+{!P6YAAsup&pWMx6%6!|rYm;LsZuQ@R(=R6B0N1Z;fIeV+>mBoy{k9ZQ# zE?fKNfnRHf$pM+Q&L`@hIfgjOs>Kw_8VP;!V_F+^srLT|g;O2QjvwA^nE!R=wVY2+ zr&XMp^E7y#{E@Rco?kAnFf8rVw)`KtD|wmTJL`9hceUIWef{}1_2;Fazh#$nz8v?? zdGY;Qe$@2zNX>3Rodco$A5N&uO?xFju|B!&dj75Isn-n8W*a|Vy=vF4>04au_wW4p zYjUKI*vnND_p)A-oX0!2pX!`nHkk9&{Ed(S2^g#g_l)D9@{gYS7~wo;uKB&c`^1#=#8wu z3e)>_RAx%e;IGr$_ELVLQ~Uf4X|L1|9d+4X(97x*`r_t_xrvG=uVl3deEI%SulRq5h_7Kp^j&WQ^Ems7sjpT|^zbj;YI9J0 z9plawRjbr*FW3ln`3$Q`XR{C8KCfKCH9v<@ZA-I%<=gsmAGOvr`&YiJKc^{u^opcs z(ThnrFZ}ObH922 z6V=?5<9{jAO!q~+!2iDt6>J65>UUS|QJAw$HSf^#yyrQ0_bOFay^X%Gf5EdC>v9rK zG0Z#jYSJ%{`)!>wKkU418C|~q#8db3NYib5XSC%pq@2?B`Z|$kiJ|bnGVAo;??gEx zx9KMQ7gx{QVOMq-YM$chZNA*U7N2VBDUINo|EhbnTFtw%bJy-_3vJ4Y-nO7VH+=d&y?oF?y5mvnoC>wwP%T+mVX?x``u3N&#~3Bi@KDK_ahR7n@y=i;O*@ zw|VQ|r6undRhhkd9`bSHwx{7qOL)q*vazprJhiWKqps5w-6dVAxy@5UduFZ;=Bk$2 zn||GJ=G(u8frUyRn@(-x2;A22VHMkRy_lGjt?Ap|U61W(k6Pw;<3AHyh!Rg?{G5vw zdHK%j`LnNza@(CuJCU<@Vf?eZLN~X+{LjQT@qJXrLYY!%DRh({mbVT@&7^Gd?hs zH}1=+xbGm~ZZ@&_>7?jFUwKAx zDQAb6{NoH$h6&G}(dhFo7!2- z^Oh?<#~V0pTHu2_lQ`Sv>)4%DSTiw_E97|V%%065!2-txLQc0l$*$(RucWek)+{c* zSuJ((cMqv%ohWDZa4S*qSW*(kabd}{-~YqrP5rt|vg31oF~{-3S&kOHj$1z7@Gfcn za$Vol&(~4nRF;nYG=6sO-%Kv)trJ$fzjVoH)r;T<_h%p2yy?TAcxQ&Juz6Nr8B~r( zR_a_!idFpk#kqHL3qy-@gX5;+ZwyOj&raAG^gL>>oYuvIg;#bioaK6sH`DcYiGf~W zqbX#Cr`lA$^TnJlSMCOJuF%MQSoxHT^_06N`@uLXsTJ`?2OYLNsNob{xY%py#brV( z{=78h&R;t>R{4nL!X4LM#9!OjII+9K#_sPn!GftLH^&HjopLj_EuR|qapJlxt|U{2 z0Jl`#E4ErU8oqR9aX49sWcAHze7L!e!ka1jNTOfhr$U0qajB?+=aNnnJG8be`89t^xTeIzoVQvfhb0=iTJCoS zciViHSval2!Oq%tl`ez1g!8O*fsJixKE^`c`{rF-$hOo!$XVdj!i)c{mh~?#XgJq> z_-%%;OsQdp)%IAqn>Ph#a)?~)=_x!?Gg*UKBi^g>$;4Uvtr;%4%?_AYwzorxZ&kpb zi#LUPTz3ER(YvI~Cu?|Kah_|fy0`6a!3Hx0-9UDO^0^21e)aFZzDl#hRb|p+zMzN6 z>o}(wFJ^fVxtU`#Bg0eWM?pF&J0<^CFrC#lc1O*nKp=b7b$$eRUM z?7R7Wyp05En^ajA$h0r3JK4a~^WpHZPZRw9g({~knz!VH;Y%O27Ki8-4eKd2O*>w! zUy?D^MKI1t=JIF0N0Vw!HeGrlRQt7YZP@z+(`S}PHuvnCJoR3HFvH?$3Ui#zyAGDE zh|}jg{j%lrH#;9|6U{4MH`y;e^n1mQYo;MuoW4#Abe*Qi`utmQ`M=!~*V?M*v%FSt z#OS~4e*H6Q(=?4o%tiGz)$;@5oDO<_x!HW-oQ-#d!#1feW%EmFvNVI*E-njN)l~WX z*4x_1|LQm0lFogUc=_Ur;LKtduDP4p8zL;rZ(Q2=wpLYP*||?MKhKADsX@cP{(X9W zEH4?mz6;-4BvZ^}cc+@+=IwMLz5h>U=9T<; z`{pv&uETbB8~da8bUm4{;3ngW1GCTch?JP`dz#Z*K6U%GcCO8bp3eVsX}h%@PhK^2 zQRbuf-=F9Uu$jksE{zH66V&_vWZJs=EQyLq@(q7Js`D`CYbjrFQm`= z(8FUYG5;h#-xuWE>3D70EZ1{8)h17At!+$dN(t$+2;ML;ZoQ0YmA0L|I8XViR~=>an9dz?5uyYSaq<@KK;Xok8_sKiBHLASv(&L#4qIB zG@s#M_KA5@+pktn4NtkfQ!z~X+bmaS$46noDd+4Qc^|CxPG-TJ-YKW%Zcetu9-jUgI3s2eq!(V#41E!MZ)A%tO7G&@oW5IqT3@d8 z#S2y|_3x@iewp0XbZqhVST;$`Q_Za1&4jMT zt7|^FSaTrYc0}ds`IrBloyv3g$YS|N*(IwNfKzagep!{?W{vrx@xABn-3vc7YsuwX z8}ubP#Fo}>l3Ls1I!V)_H|k08zmENJvy_S_{TJEu`|*~xH!r#$wK1(cR_&$s=(76F zqK4ul(`)XW|892v2siWgbd_DIpEUOsNK|OwjQTFN0v!GSJp3hoOiWj<{_Rs2BC(@K z=e)XKcDvkDi|4#hZP}SKn$*Z#U!2iFOHU&mj^cVb0H@j?b|NBoy zy$6SNUlx8T^6Gg!xq<(?*owl`c#r{lZ?2i#TNZuJgW+v2>+_2L>+`ejcGs{qd^7Vq z{!^>?3-61&o0uGSrzAG|Z#Z$q$3G_XqH?}Y!M65gA0OL2`Q8}1xVU?N8|N{`^!F;e z4rj}Yt~cDj!hwG*tDEd%&{`XNwi7FlT`OR;P fe^_6*@ko75Rs8vNKlr}ieD=xF{(H>d+kp%KCDrsP literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/src/qtdesignstudio-getting-started.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-getting-started.qdoc index c10ee9d3304..493242254f8 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-getting-started.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-getting-started.qdoc @@ -21,7 +21,7 @@ \endtable \list - \li \l {Installation} + \li \l {Installing \QDS} \QDS is available either as a standalone installation package or as an option in \QOI. diff --git a/doc/qtdesignstudio/src/qtdesignstudio-installation.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-installation.qdoc index 2ee66d9e15c..28a9d7cd091 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-installation.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-installation.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! @@ -6,44 +6,51 @@ \previouspage studio-getting-started.html \nextpage {Tutorials} - \title Installation + \title Installing \QDS - You can install \QDS either using a stand-alone installation package or \QOI. - The installers copy all the modules and tools you need - to design UIs and preview them on the desktop to your computer and configure - them for you. \QDS is available for Linux, \macOS, and Windows operating - systems. For more information, see \l{Supported Platforms}. + Install \QDS using \QOI. If you have Qt installed, install \QDS using + \QMT. If your business wants to explore \QDS, get + the free Qt evaluation package. If you're a student or a teacher, + get \QDS using the Qt Edu license. - To begin, create a \l{Qt Account}. This account gives you access to a web - portal to manage your licenses and download the standalone \QDS package or - \QOI. Alternatively, you can download an evaluation - package \l{https://www.qt.io/product/ui-design-tools}{here}. - - After the installation, you can start exploring \QDS by following - \l{Tutorials}{tutorials}, opening - \l{Examples}{examples}, watching videos, and reading - this manual. - - \section1 Stand-Alone Installation - - You can download the \QDS installer for your operating system (\macos, - Linux, or Windows) from your Qt Account. Start the installation as you - would for any other software, and follow the instructions of the - installer to complete it. + \QDS is available on several operating systems. For more information, see + \l{Supported Platforms}. \section1 Using \QOI - You can download \QOI for your operating system - from your Qt Account. + Download \QOI from your Qt Account. If you don't have a Qt Account, + create one \l {Qt Account}{here}. \list 1 - \li Start \QOI. - \li Select \uicontrol {Design Tools}. - \image studio-installation.png "Design Tools selected in Qt Online Installer" + \li Open \QOI. + \li Select \uicontrol {\QDS}. + \image studio-installation.webp "Qt Design Studio selected in Qt Online Installer" \li Select \uicontrol Next and follow the instructions of the installer to complete the installation. \endlist - For more information about the installation process, see - \l{Getting Started with Qt}. + If you can't use \QOI, use \QDS offline installer instead. Get \QDS offline installer + from your Qt Account under \uicontrol Downloads > \uicontrol Products. + + \section1 Using \QMT + + \QMT is included in each Qt installation. If you already have Qt, use \QMT to install \QDS. + + \list 1 + \li Open \QMT. + \li Select \uicontrol {Add or remove components}. + \li Select \uicontrol {\QDS}. + \li Select \uicontrol Next and follow the instructions of the installer to complete + the installation. + \endlist + + \section1 Using Qt Evaluation + + Businesses can get a free Qt evaluation package. To start the trial of \QDS, fill the + evaluation form \l{Try Qt}{here}. + + \section1 Using Qt Edu license + + Students and teachers can get a licensing package for learning purposes. + To apply for the educational license and install \QDS, see \l {Qt Edu for Designers}. */ diff --git a/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc index 7d423581450..fe5d342e47b 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc @@ -8,7 +8,7 @@ \list \li \l{Getting Started} \list - \li \l{Installation} + \li \l{Installing \QDS} \li \l{Tutorials} \li \l{User Interface} \list diff --git a/doc/qtdesignstudio/src/qtdesignstudio.qdoc b/doc/qtdesignstudio/src/qtdesignstudio.qdoc index 600bc9427a2..6ecd9c9241b 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio.qdoc @@ -25,7 +25,7 @@ \row \li \b {\l{Getting Started}} \list - \li \l{Installation} + \li \l{Installing \QDS} \li \l{Tutorials} \li \l{User Interface} \li \l{Creating Projects} From 8f33db6bf1348b1d4afb668f49d0ba9b67a26dba Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Fri, 8 Mar 2024 11:46:53 +0200 Subject: [PATCH 162/176] QmlDesigner: Fix the bug for regexp and encoding of csv in ModelEditor Fixes: QDS-11667 Change-Id: I2399f1a8689634f92595624f4da781f27290fb57 Reviewed-by: Mahmoud Badri Reviewed-by: Qt CI Patch Build Bot --- .../collectioneditor/collectiondetails.cpp | 41 +++++++++---------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp index 810923863bb..97304c3964d 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp @@ -54,9 +54,8 @@ public: inline static bool isValidColorName(const QString &colorName) { - constexpr QStringView colorPattern( - u"(?^(?:#(?:(?:[0-9a-fA-F]{2}){3,4}|(?:[0-9a-fA-F]){3,4}))$)"); - static const QRegularExpression colorRegex(colorPattern.toString()); + static const QRegularExpression colorRegex{ + "(?^(?:#(?:(?:[0-9a-fA-F]{2}){3,4}|(?:[0-9a-fA-F]){3,4}))$)"}; return colorRegex.match(colorName).hasMatch(); } @@ -79,19 +78,19 @@ inline static bool getCustomUrl(const QString &value, QUrl *urlResult = nullptr, QString *subType = nullptr) { - constexpr QStringView urlPattern( - u"^(?" - u"(?image)\\/" - u"(?apng|avif|gif|jpeg|png|(?:svg\\+xml)|webp|xyz)\\:)?" // end of MimeType - u"(?
    " - u"(?https?:\\/\\/" - u"(?:www\\.|(?!www))[A-z0-9][A-z0-9-]+[A-z0-9]\\.[^\\s]{2,}|www\\.[A-z0-9][A-z0-9-]+" - u"[A-z0-9]\\.[^\\s]{2,}|https?:\\/\\/" - u"(?:www\\.|(?!www))[A-z0-9]+\\.[^\\s]{2,}|www\\.[A-z0-9]+\\.[^\\s]{2,})|" // end of Url - u"(?(" - u"?:(?:[A-z]:)|(?:(?:\\\\|\\/){1,2}\\w+)\\$?)(?:(?:\\\\|\\/)(?:\\w[\\w ]*.*))+)" // end of LocalFile - u"){1}$"); // end of Address - static const QRegularExpression urlRegex(urlPattern.toString()); + static const QRegularExpression urlRegex{ + "^(?" + "(?image)\\/" + "(?apng|avif|gif|jpeg|png|(?:svg\\+xml)|webp|xyz)\\:)?" // end of MimeType + "(?
    " + "(?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?:\\/\\/" + "(?:www\\.|(?!www))[A-z0-9]+\\.[^\\s]{2,}|www\\.[A-z0-9]+\\.[^\\s]{2,})|" // end of Url + "(?(" + "?:(?:[A-z]:)|(?:(?:\\\\|\\/){1,2}\\w+)\\$?)(?:(?:\\\\|\\/)(?:\\w[\\w ]*.*))+)" // end of LocalFile + "){1}$" // end of Address + }; const QRegularExpressionMatch match = urlRegex.match(value); if (match.hasMatch()) { @@ -267,10 +266,9 @@ inline static bool isEmptyJsonValue(const QJsonValue &value) QStringList csvReadLine(const QString &line) { - constexpr QStringView linePattern = u"(?:,\"|^\")(?\"\"|[\\w\\W]*?)(?=\",|\"$)" - u"|(?:,(?!\")|^(?!\"))(?[^,]*?)(?=$|,)|(\\r\\n|\\n)"; - - static const QRegularExpression lineRegex(linePattern.toString()); + static const QRegularExpression lineRegex{ + "(?:,\\\"|^\\\")(?\\\"\\\"|[\\w\\W]*?)(?=\\\",|\\\"$)" + "|(?:,(?!\\\")|^(?!\\\"))(?[^,]*?)(?=$|,)|(\\\\r\\\\n|\\\\n)"}; static const int valueIndex = lineRegex.namedCaptureGroups().indexOf("value"); static const int quoteIndex = lineRegex.namedCaptureGroups().indexOf("quote"); Q_ASSERT(valueIndex > 0 && quoteIndex > 0); @@ -281,7 +279,7 @@ QStringList csvReadLine(const QString &line) const QRegularExpressionMatch match = iterator.next(); if (match.hasCaptured(valueIndex)) - result.append(match.captured(2)); + result.append(match.captured(valueIndex)); else if (match.hasCaptured(quoteIndex)) result.append(match.captured(quoteIndex)); } @@ -737,6 +735,7 @@ CollectionDetails CollectionDetails::fromImportedCsv(const QByteArray &document, QJsonArray importedArray; QTextStream stream(document); + stream.setEncoding(QStringConverter::Latin1); if (firstRowIsHeader && !stream.atEnd()) { headers = Utils::transform(csvReadLine(stream.readLine()), From b5f7b4c45e01980964f76cdbbd5e76021ee3022c Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Tue, 12 Mar 2024 15:43:48 +0200 Subject: [PATCH 163/176] QmlDesigner: Remove the taskbar window when popup opens Change-Id: I58f52983d8f939f489d3ff364705660906253289 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Miikka Heikkinen --- .../effectComposerQmlSources/EffectNodesComboBox.qml | 2 +- .../effectComposerQmlSources/PreviewImagesComboBox.qml | 2 +- .../imports/StudioControls/PopupDialog.qml | 2 +- .../imports/StudioControls/TopLevelComboBox.qml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectNodesComboBox.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectNodesComboBox.qml index 7051a9292fc..d829f42318b 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectNodesComboBox.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectNodesComboBox.qml @@ -74,7 +74,7 @@ StudioControls.ComboBox { Window { id: window - flags: Qt.Dialog | Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint + flags: Qt.Tool | Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint onActiveFocusItemChanged: { if (!window.activeFocusItem && !root.hovered && root.popup.opened) diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/PreviewImagesComboBox.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/PreviewImagesComboBox.qml index 2a35606a14a..201fab9699d 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/PreviewImagesComboBox.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/PreviewImagesComboBox.qml @@ -108,7 +108,7 @@ StudioControls.ComboBox { Window { id: window - flags: Qt.Dialog | Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint + flags: Qt.Tool | Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint onActiveFocusItemChanged: { if (!window.activeFocusItem && !root.hovered && root.popup.opened) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/PopupDialog.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/PopupDialog.qml index acf805928e7..40c35df27c7 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/PopupDialog.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/PopupDialog.qml @@ -104,7 +104,7 @@ QtObject { return root.maximumHeight + (2 * window.margin) } visible: false - flags: Qt.FramelessWindowHint | Qt.Dialog | Qt.WindowStaysOnTopHint + flags: Qt.FramelessWindowHint | Qt.Tool | Qt.WindowStaysOnTopHint color: "transparent" onClosing: function (close) { diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TopLevelComboBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TopLevelComboBox.qml index 27f8333d246..284e32a7b25 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TopLevelComboBox.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TopLevelComboBox.qml @@ -110,7 +110,7 @@ T.ComboBox { width: control.listView.width height: control.listView.height + 2 * control.style.borderWidth visible: false - flags: Qt.FramelessWindowHint | Qt.Dialog | Qt.NoDropShadowWindowHint | Qt.WindowStaysOnTopHint + flags: Qt.FramelessWindowHint | Qt.Tool | Qt.NoDropShadowWindowHint | Qt.WindowStaysOnTopHint modality: Qt.NonModal transientParent: control.Window.window color: "transparent" From 170aefb0ddb050fc80d14b3b944ced561adcc04f Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Tue, 12 Mar 2024 12:36:54 +0100 Subject: [PATCH 164/176] QmlDesigner: Crash fix We have to check if the semantic info is actually valid. Task-number: QDS-12203 Change-Id: Ia4fa411708f0235de4754ced057fb86dd9a4aa7a Reviewed-by: Tim Jenssen --- .../components/bindingeditor/bindingeditorwidget.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/qmldesigner/components/bindingeditor/bindingeditorwidget.cpp b/src/plugins/qmldesigner/components/bindingeditor/bindingeditorwidget.cpp index aa3b883b9ae..ff2361aa36c 100644 --- a/src/plugins/qmldesigner/components/bindingeditor/bindingeditorwidget.cpp +++ b/src/plugins/qmldesigner/components/bindingeditor/bindingeditorwidget.cpp @@ -132,14 +132,14 @@ void BindingDocument::applyFontSettings() { TextDocument::applyFontSettings(); m_semanticHighlighter->updateFontSettings(fontSettings()); - if (!isSemanticInfoOutdated()) + if (!isSemanticInfoOutdated() && semanticInfo().isValid()) m_semanticHighlighter->rerun(semanticInfo()); } void BindingDocument::triggerPendingUpdates() { TextDocument::triggerPendingUpdates(); // calls applyFontSettings if necessary - if (!isSemanticInfoOutdated()) + if (!isSemanticInfoOutdated() && semanticInfo().isValid()) m_semanticHighlighter->rerun(semanticInfo()); } From 4b035bd4cbbda95da995d0b85209a629a135032f Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Wed, 13 Mar 2024 09:54:21 +0200 Subject: [PATCH 165/176] QmlDesigner: Fix the tooltip for integer in Model Editor Task-number: QDS-11676 Change-Id: I6eb531dae75126ad83e5f744313d801810c56678 Reviewed-by: Mats Honkamaa --- .../components/collectioneditor/collectiondatatypemodel.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondatatypemodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondatatypemodel.cpp index b588f3be566..cb306c58cb9 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondatatypemodel.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondatatypemodel.cpp @@ -17,13 +17,13 @@ struct CollectionDataTypeModel::Details const QList CollectionDataTypeModel::m_orderedDetails{ {DataType::String, "String", "Text"}, - {DataType::Integer, "Integer", "Whole number"}, + {DataType::Integer, "Integer", "Whole number that can be positive, negative, or zero"}, {DataType::Real, "Real", "Number with a decimal"}, {DataType::Image, "Image", "Image resource"}, {DataType::Color, "Color", "HEX value"}, {DataType::Url, "Url", "Resource locator"}, - {DataType::Boolean, "Boolean", "True/False"}, - {DataType::Unknown, "Unknown", "Unknown Data Type"}, + {DataType::Boolean, "Boolean", "True/false"}, + {DataType::Unknown, "Unknown", "Unknown data type"}, }; CollectionDataTypeModel::CollectionDataTypeModel(QObject *parent) From f3782aed90bf2d5707e902142b7f60b23071a2dd Mon Sep 17 00:00:00 2001 From: Shrief Gabr Date: Wed, 13 Mar 2024 03:32:08 +0200 Subject: [PATCH 166/176] EffectComposer: Prevent overwriting when creating a new composition Task-number: QDS-12144 Change-Id: Ie54ef39172969c9bfb008d70169a439b992f52fd Reviewed-by: Mahmoud Badri Reviewed-by: Qt CI Patch Build Bot --- .../qmldesigner/effectComposerQmlSources/SaveAsDialog.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/SaveAsDialog.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/SaveAsDialog.qml index d1bb00d9237..d139ba92055 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/SaveAsDialog.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/SaveAsDialog.qml @@ -90,7 +90,7 @@ StudioControls.Dialog { EffectComposerBackend.effectComposerModel.saveComposition(nameText.text) if (root.clearOnClose) { - EffectComposerBackend.effectComposerModel.clear() + EffectComposerBackend.effectComposerModel.clear(true) root.clearOnClose = false } From fbb8bd89f9a0e314a1ec8fee385b5babc0847caa Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Wed, 13 Mar 2024 14:35:22 +0200 Subject: [PATCH 167/176] QmlDesigner: Set null value for the empty properties of json objects Fixes: QDS-12211 Change-Id: I0b85c6fb2f059317ab412f083e2130e8c1cfb6b6 Reviewed-by: Shrief Gabr Reviewed-by: Mahmoud Badri --- .../components/collectioneditor/collectiondetails.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp index 97304c3964d..54c6b01709c 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp @@ -643,7 +643,9 @@ QString CollectionDetails::toJson() const QJsonObject exportedElement; for (int i = 0; i < valueCount; ++i) { const QJsonValue &value = record.at(i); - if (!isEmptyJsonValue(value)) + if (isEmptyJsonValue(value)) + exportedElement.insert(d->properties.at(i).name, QJsonValue::Null); + else exportedElement.insert(d->properties.at(i).name, value); } From d90828b055540e7d17bed8a8af538ffc44beb41c Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Wed, 13 Mar 2024 13:10:10 +0200 Subject: [PATCH 168/176] QmlDesigner: Detect literal types from string values in ModelEditor Fixes: QDS-12213 Change-Id: I58d56c42b94be99309a344e084c68703b0b25b82 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri --- .../collectioneditor/collectiondetails.cpp | 87 +++++++++++++------ .../collectiondetailsmodel.cpp | 27 ------ .../collectioneditor/collectiondetailsmodel.h | 1 - 3 files changed, 61 insertions(+), 54 deletions(-) diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp index 54c6b01709c..80e223d2ec9 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -52,13 +53,6 @@ public: bool isValidRowId(int row) const { return row > -1 && row < dataRecords.size(); } }; -inline static bool isValidColorName(const QString &colorName) -{ - static const QRegularExpression colorRegex{ - "(?^(?:#(?:(?:[0-9a-fA-F]{2}){3,4}|(?:[0-9a-fA-F]){3,4}))$)"}; - return colorRegex.match(colorName).hasMatch(); -} - /** * @brief getCustomUrl * MimeType = @@ -73,10 +67,10 @@ inline static bool isValidColorName(const QString &colorName) * will be stored in this parameter, otherwise it will be empty. * @return true if the result is either url or image */ -inline static bool getCustomUrl(const QString &value, - CollectionDetails::DataType &dataType, - QUrl *urlResult = nullptr, - QString *subType = nullptr) +static bool getCustomUrl(const QString &value, + CollectionDetails::DataType &dataType, + QUrl *urlResult = nullptr, + QString *subType = nullptr) { static const QRegularExpression urlRegex{ "^(?" @@ -120,6 +114,60 @@ inline static bool getCustomUrl(const QString &value, return false; } +/** + * @brief dataTypeFromString + * @param value The string value to be evaluated + * @return Unknown if the string is empty, But returns Bool, Color, Integer, + * Real, Url, Image if these types are detected within the non-empty string, + * Otherwise it returns String. + * If the value is integer, but it's out of the int range, it will be + * considered as a Real. + */ +static CollectionDetails::DataType dataTypeFromString(const QString &value) +{ + using DataType = CollectionDetails::DataType; + static const QRegularExpression validator{ + "(?^(?:true|false)$)|" + "(?^(?:#(?:(?:[0-9a-fA-F]{2}){3,4}|(?:[0-9a-fA-F]){3,4}))$)|" + "(?^\\d+$)|" + "(?^(?:-?(?:0|[1-9]\\d*)?(?:\\.\\d*)?(?<=\\d|\\.)" + "(?:e-?(?:0|[1-9]\\d*))?|0x[0-9a-f]+)$)"}; + static const int boolIndex = validator.namedCaptureGroups().indexOf("boolean"); + static const int colorIndex = validator.namedCaptureGroups().indexOf("color"); + static const int integerIndex = validator.namedCaptureGroups().indexOf("integer"); + static const int realIndex = validator.namedCaptureGroups().indexOf("real"); + + [[maybe_unused]] static const bool allIndexesFound = + [](const std::initializer_list &captureIndexes) { + QTC_ASSERT(Utils::allOf(captureIndexes, [](int val) { return val > -1; }), return false); + return true; + }({boolIndex, colorIndex, integerIndex, realIndex}); + + if (value.isEmpty()) + return DataType::Unknown; + + const QString trimmedValue = value.trimmed(); + QRegularExpressionMatch match = validator.match(trimmedValue); + + if (match.hasCaptured(boolIndex)) + return DataType::Boolean; + if (match.hasCaptured(colorIndex)) + return DataType::Color; + if (match.hasCaptured(integerIndex)) { + bool isInt = false; + trimmedValue.toInt(&isInt); + return isInt ? DataType::Integer : DataType::Real; + } + if (match.hasCaptured(realIndex)) + return DataType::Real; + + DataType urlType; + if (getCustomUrl(value, urlType)) + return urlType; + + return DataType::String; +} + static CollectionProperty::DataType dataTypeFromJsonValue(const QJsonValue &value) { using DataType = CollectionDetails::DataType; @@ -136,21 +184,8 @@ static CollectionProperty::DataType dataTypeFromJsonValue(const QJsonValue &valu return DataType::Integer; return DataType::Real; } - case JsonType::String: { - const QString stringValue = value.toString(); - - if (stringValue.isEmpty()) - return DataType::Unknown; - - if (isValidColorName(stringValue)) - return DataType::Color; - - DataType urlType; - if (getCustomUrl(stringValue, urlType)) - return urlType; - - return DataType::String; - } break; + case JsonType::String: + return dataTypeFromString(value.toString()); default: return DataType::Unknown; } diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp index 326afb203ae..b26b1a845e9 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp @@ -584,33 +584,6 @@ void CollectionDetailsModel::ensureSingleCell() updateEmpty(); } -QVariant CollectionDetailsModel::variantFromString(const QString &value) -{ - constexpr QStringView typesPattern{u"(?^(?:true|false)$)|" - u"(?^(?:-?(?:0|[1-9]\\d*)?(?:\\.\\d*)?(?<=\\d|\\.)" - u"(?:e-?(?:0|[1-9]\\d*))?|0x[0-9a-f]+)$)|" - u"(?^(?:#(?:(?:[0-9a-fA-F]{2}){3,4}|" - u"(?:[0-9a-fA-F]){3,4}))$)|" - u"(?[A-Za-z][A-Za-z0-9_ -]*)"}; - static QRegularExpression validator(typesPattern.toString()); - const QString trimmedValue = value.trimmed(); - QRegularExpressionMatch match = validator.match(trimmedValue); - QVariant variantValue = value; - - if (value.isEmpty()) - return QVariant(); - if (!match.captured(u"boolean").isEmpty()) - return variantValue.toBool(); - if (!match.captured(u"number").isEmpty()) - return variantValue.toDouble(); - if (!match.captured(u"color").isEmpty()) - return variantValue.value(); - if (!match.captured(u"string").isEmpty()) - return variantValue.toString(); - - return QVariant::fromValue(value); -} - QJsonDocument CollectionDetailsModel::readJsonFile(const QUrl &url) { using Utils::FilePath; diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h index 0ad6e86f10c..24a040cce6c 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h @@ -88,7 +88,6 @@ private: void setCollectionName(const QString &newCollectionName); void loadJsonCollection(const QString &filePath, const QString &collection); void ensureSingleCell(); - QVariant variantFromString(const QString &value); QJsonDocument readJsonFile(const QUrl &url); QHash m_openedCollections; From 9780e05ef274e60a68c733385f2f14b53a3dc224 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Wed, 13 Mar 2024 15:55:13 +0200 Subject: [PATCH 169/176] QmlDesigner: Check the validity before accepting the import dialog Fixes: QDS-12068 Change-Id: I5341214b43edf8fb7d3693a861a09c7fd3461ac2 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri --- .../collectionEditorQmlSource/ImportDialog.qml | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/ImportDialog.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/ImportDialog.qml index d98b23c5ba7..21a5e4e8e04 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/ImportDialog.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/ImportDialog.qml @@ -32,6 +32,11 @@ StudioControls.Dialog { fileName.text = "" } + function acceptIfIsValid() { + if (btnImport.enabled) + btnImport.onClicked() + } + RegularExpressionValidator { id: fileNameValidator regularExpression: /^(\w[^*> Date: Wed, 13 Mar 2024 16:17:08 +0200 Subject: [PATCH 170/176] QmlDesigner: Add Ids to icons for Squish tests Task-number: QDS-12204 Change-Id: Ib63409e9092db4446756ec9dec6092947d5f0697 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri --- .../CollectionDetailsToolbar.qml | 16 ++++++++++++++++ .../CollectionDetailsView.qml | 6 ++++++ .../collectionEditorQmlSource/CollectionView.qml | 2 ++ 3 files changed, 24 insertions(+) diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml index 7a5e77f42fb..8b4c08f8458 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml @@ -46,6 +46,8 @@ Rectangle { spacing: StudioTheme.Values.sectionRowSpacing IconButton { + id: addColumnLeftButton + buttonIcon: StudioTheme.Constants.addcolumnleft_medium tooltip: qsTr("Add property left") enabled: root.model.selectedColumn > -1 @@ -53,6 +55,8 @@ Rectangle { } IconButton { + id: addColumnRightButton + buttonIcon: StudioTheme.Constants.addcolumnright_medium tooltip: qsTr("Add property right") enabled: root.model.selectedColumn > -1 @@ -60,6 +64,8 @@ Rectangle { } IconButton { + id: deleteColumnButton + buttonIcon: StudioTheme.Constants.deletecolumn_medium tooltip: qsTr("Delete selected property") enabled: root.model.selectedColumn > -1 @@ -72,6 +78,8 @@ Rectangle { } IconButton { + id: addRowBelowButton + buttonIcon: StudioTheme.Constants.addrowbelow_medium tooltip: qsTr("Insert row below") enabled: root.model.selectedRow > -1 @@ -79,6 +87,8 @@ Rectangle { } IconButton { + id: addRowAboveButton + buttonIcon: StudioTheme.Constants.addrowabove_medium tooltip: qsTr("Insert row above") enabled: root.model.selectedRow > -1 @@ -86,6 +96,8 @@ Rectangle { } IconButton { + id: deleteSelectedRowButton + buttonIcon: StudioTheme.Constants.deleterow_medium tooltip: qsTr("Delete selected row") enabled: root.model.selectedRow > -1 @@ -101,6 +113,8 @@ Rectangle { Layout.rightMargin: StudioTheme.Values.toolbarHorizontalMargin IconButton { + id: saveCollectionButton + buttonIcon: StudioTheme.Constants.save_medium tooltip: qsTr("Save changes") enabled: root.model.collectionName !== "" @@ -108,6 +122,8 @@ Rectangle { } IconButton { + id: exportCollectionButton + buttonIcon: StudioTheme.Constants.export_medium tooltip: qsTr("Export model") enabled: root.model.collectionName !== "" diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml index b4443f53b4e..c9a6d2dbcbd 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml @@ -395,16 +395,22 @@ Rectangle { } CellContextMenuItem { + id: addRowAboveCellMenuItem + itemText: qsTr("Add row above") itemIcon: StudioTheme.Constants.addrowabove_medium onTriggered: root.model.insertRow(cellContextMenu.rowIndex) } CellContextMenuItem { + id: addRowBelowCellMenuItem + itemText: qsTr("Add row below") itemIcon: StudioTheme.Constants.addrowbelow_medium onTriggered: root.model.insertRow(cellContextMenu.rowIndex + 1) } CellContextMenuItem { + id: deleteRowCellMenuItem + itemText: qsTr("Delete row") itemIcon: StudioTheme.Constants.deleterow_medium onTriggered: root.model.removeRows(cellContextMenu.rowIndex, 1) diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml index b9f8d9a532f..acf82fe4527 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml @@ -132,6 +132,8 @@ Item { } HelperWidgets.AbstractButton { + id: importCollectionButton + anchors.verticalCenter: parent.verticalCenter anchors.right: parent.right anchors.rightMargin: StudioTheme.Values.toolbarHorizontalMargin From 5a85490d4c3f042fe9faa9c0f11a4181686ab168 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Mon, 11 Mar 2024 17:25:43 +0200 Subject: [PATCH 171/176] QmlDesigner: Add 3D edit camera speed configuration dialog Edit camera speed configuration consists of a slider and a multiplier, which combine into the total camera speed. Speed and multiplier are stored per scene. Fixes: QDS-12187 Change-Id: I587c04cf80d1ca95b5a38c406158e46cdb91d06f Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Reviewed-by: Mahmoud Badri --- .../CameraSpeedConfigurationDialog.qml | 156 ++++++++++++++ src/plugins/qmldesigner/CMakeLists.txt | 1 + .../components/componentcore/utils3d.cpp | 32 +++ .../components/componentcore/utils3d.h | 3 + .../components/edit3d/bakelights.cpp | 34 +--- .../components/edit3d/bakelights.h | 3 - .../components/edit3d/bakelightsdatamodel.cpp | 4 +- .../edit3d/cameraspeedconfiguration.cpp | 192 ++++++++++++++++++ .../edit3d/cameraspeedconfiguration.h | 76 +++++++ .../components/edit3d/edit3dactions.cpp | 4 +- .../components/edit3d/edit3dview.cpp | 70 +++++++ .../components/edit3d/edit3dview.h | 9 + .../qmldesigner/qmldesignerconstants.h | 1 + .../mockfiles/qt6/EditCameraController.qml | 2 +- .../qml2puppet/editor3d/generalhelper.cpp | 8 + .../qml2puppet/editor3d/generalhelper.h | 5 + .../qt5informationnodeinstanceserver.cpp | 8 +- .../qt5informationnodeinstanceserver.h | 2 +- 18 files changed, 567 insertions(+), 43 deletions(-) create mode 100644 share/qtcreator/qmldesigner/edit3dQmlSource/CameraSpeedConfigurationDialog.qml create mode 100644 src/plugins/qmldesigner/components/edit3d/cameraspeedconfiguration.cpp create mode 100644 src/plugins/qmldesigner/components/edit3d/cameraspeedconfiguration.h diff --git a/share/qtcreator/qmldesigner/edit3dQmlSource/CameraSpeedConfigurationDialog.qml b/share/qtcreator/qmldesigner/edit3dQmlSource/CameraSpeedConfigurationDialog.qml new file mode 100644 index 00000000000..219b485ca0d --- /dev/null +++ b/share/qtcreator/qmldesigner/edit3dQmlSource/CameraSpeedConfigurationDialog.qml @@ -0,0 +1,156 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import HelperWidgets as HelperWidgets +import StudioControls as StudioControls +import StudioTheme as StudioTheme + +Rectangle { + id: root + + property int toolTipDelay: 1000 + + width: 260 + height: 150 + color: StudioTheme.Values.themePanelBackground + border.color: StudioTheme.Values.themeControlOutline + border.width: StudioTheme.Values.border + + function handleSpeedChanged() { + speedSlider.value = Math.round(speed) + } + + function handleMultiplierChanged() { + multiplierSpin.value = multiplier + } + + // Connect context object signals to our handler functions + // Controls lose the initial binding if the value changes so we need these handlers + Component.onCompleted: { + onSpeedChanged.connect(handleSpeedChanged); + onMultiplierChanged.connect(handleMultiplierChanged); + } + + ColumnLayout { + anchors.fill: parent + spacing: 0 + + RowLayout { + height: 32 + Layout.topMargin: 8 + Layout.rightMargin: 8 + Layout.leftMargin: 8 + Layout.fillWidth: true + spacing: 16 + + Rectangle { + width: 40 + height: 40 + radius: 5 + Layout.fillHeight: false + color: StudioTheme.Values.themePanelBackground + border.color: StudioTheme.Values.themeControlOutline + border.width: StudioTheme.Values.border + + HelperWidgets.IconIndicator { + anchors.fill: parent + icon: StudioTheme.Constants.snapping_conf_medium // TODO update icon + pixelSize: StudioTheme.Values.myIconFontSize * 1.4 + iconColor: StudioTheme.Values.themeLinkIndicatorColorHover + enabled: false + states: [] // Disable normal state based coloring + } + } + Text { + text: qsTr("Camera Speed Configuration") + font.pixelSize: 12 + horizontalAlignment: Text.AlignLeft + Layout.fillWidth: true + font.bold: true + color: StudioTheme.Values.themeTextColor + } + } + + ColumnLayout { + Layout.margins: 10 + Layout.fillWidth: true + Layout.fillHeight: true + + RowLayout { + Layout.fillWidth: true + spacing: 5 + + StudioControls.Slider { + id: speedSlider + Layout.fillWidth: true + labels: false + actionIndicatorVisible: false + handleLabelVisible: false + from: 1 + to: 100 + value: Math.round(speed) + onMoved: speed = Math.round(value) + + hoverEnabled: true + ToolTip.visible: hovered + ToolTip.text: qsTr("The speed camera moves when controlled by keyboard.") + ToolTip.delay: root.toolTipDelay + } + + Text { + Layout.preferredWidth: 80 + text: { + const decimals = -Math.floor(Math.log10(multiplier)) + return totalSpeed.toLocaleString(Qt.locale(), 'f', decimals > 0 ? decimals : 0) + } + + font.pixelSize: 12 + font.bold: true + horizontalAlignment: Qt.AlignRight + color: StudioTheme.Values.themeTextColor + } + } + + RowLayout { + Layout.fillWidth: true + spacing: 5 + + Text { + text: qsTr("Multiplier") + font.pixelSize: 12 + horizontalAlignment: Text.AlignLeft + Layout.fillWidth: true + font.bold: true + color: StudioTheme.Values.themeTextColor + } + + HelperWidgets.DoubleSpinBox { + id: multiplierSpin + Layout.fillWidth: true + minimumValue: 0.01 + maximumValue: 100000 + value: multiplier + stepSize: 0.01 + decimals: 2 + + ToolTip.visible: hover + ToolTip.text: qsTr("The value multiplier for the speed slider.") + ToolTip.delay: root.toolTipDelay + + onValueChanged: multiplier = value + } + + HelperWidgets.Button { + text: qsTr("Reset") + Layout.alignment: Qt.AlignBottom + Layout.preferredWidth: 70 + Layout.preferredHeight: multiplierSpin.height + onClicked: resetDefaults() + } + } + } + } +} diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 3f503c42311..b5b64bebbc8 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -652,6 +652,7 @@ extend_qtc_plugin(QmlDesigner backgroundcolorselection.cpp backgroundcolorselection.h bakelights.cpp bakelights.h snapconfiguration.cpp snapconfiguration.h + cameraspeedconfiguration.cpp cameraspeedconfiguration.h bakelightsdatamodel.cpp bakelightsdatamodel.h bakelightsconnectionmanager.cpp bakelightsconnectionmanager.h edit3d.qrc diff --git a/src/plugins/qmldesigner/components/componentcore/utils3d.cpp b/src/plugins/qmldesigner/components/componentcore/utils3d.cpp index 54618604df2..3983dcb13ca 100644 --- a/src/plugins/qmldesigner/components/componentcore/utils3d.cpp +++ b/src/plugins/qmldesigner/components/componentcore/utils3d.cpp @@ -121,5 +121,37 @@ ModelNode getTextureDefaultInstance(const QString &source, AbstractView *view) return {}; } +ModelNode activeView3dNode(AbstractView *view) +{ + if (!view || !view->model()) + return {}; + + ModelNode activeView3D; + ModelNode activeScene = Utils3D::active3DSceneNode(view); + + if (activeScene.isValid()) { + if (activeScene.metaInfo().isQtQuick3DView3D()) { + activeView3D = activeScene; + } else { + ModelNode sceneParent = activeScene.parentProperty().parentModelNode(); + if (sceneParent.metaInfo().isQtQuick3DView3D()) + activeView3D = sceneParent; + } + return activeView3D; + } + + return {}; +} + +QString activeView3dId(AbstractView *view) +{ + ModelNode activeView3D = activeView3dNode(view); + + if (activeView3D.isValid()) + return activeView3D.id(); + + return {}; +} + } // namespace Utils3D } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/componentcore/utils3d.h b/src/plugins/qmldesigner/components/componentcore/utils3d.h index 9891a62b4cd..0e582c0fb4a 100644 --- a/src/plugins/qmldesigner/components/componentcore/utils3d.h +++ b/src/plugins/qmldesigner/components/componentcore/utils3d.h @@ -21,5 +21,8 @@ void ensureMaterialLibraryNode(AbstractView *view); bool isPartOfMaterialLibrary(const ModelNode &node); ModelNode getTextureDefaultInstance(const QString &source, AbstractView *view); +ModelNode activeView3dNode(AbstractView *view); +QString activeView3dId(AbstractView *view); + } // namespace Utils3D } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/edit3d/bakelights.cpp b/src/plugins/qmldesigner/components/edit3d/bakelights.cpp index 25a27f778bf..ab1eb9b07b1 100644 --- a/src/plugins/qmldesigner/components/edit3d/bakelights.cpp +++ b/src/plugins/qmldesigner/components/edit3d/bakelights.cpp @@ -62,7 +62,7 @@ BakeLights::BakeLights(AbstractView *view) : QObject(view) , m_view(view) { - m_view3dId = resolveView3dId(view); + m_view3dId = Utils3D::activeView3dId(view); if (m_view3dId.isEmpty()) { // It should never get here, baking controls should be disabled in this case @@ -79,38 +79,6 @@ BakeLights::~BakeLights() cleanup(); } -ModelNode BakeLights::resolveView3dNode(AbstractView *view) -{ - if (!view || !view->model()) - return {}; - - ModelNode activeView3D; - ModelNode activeScene = Utils3D::active3DSceneNode(view); - - if (activeScene.isValid()) { - if (activeScene.metaInfo().isQtQuick3DView3D()) { - activeView3D = activeScene; - } else { - ModelNode sceneParent = activeScene.parentProperty().parentModelNode(); - if (sceneParent.metaInfo().isQtQuick3DView3D()) - activeView3D = sceneParent; - } - return activeView3D; - } - - return {}; -} - -QString BakeLights::resolveView3dId(AbstractView *view) -{ - ModelNode activeView3D = resolveView3dNode(view); - - if (activeView3D.isValid()) - return activeView3D.id(); - - return {}; -} - void BakeLights::raiseDialog() { if (m_progressDialog) diff --git a/src/plugins/qmldesigner/components/edit3d/bakelights.h b/src/plugins/qmldesigner/components/edit3d/bakelights.h index 65f00acc7b0..2b6848bd482 100644 --- a/src/plugins/qmldesigner/components/edit3d/bakelights.h +++ b/src/plugins/qmldesigner/components/edit3d/bakelights.h @@ -41,9 +41,6 @@ public: bool manualMode() const; void setManualMode(bool enabled); - static ModelNode resolveView3dNode(AbstractView *view); - static QString resolveView3dId(AbstractView *view); - signals: void finished(); void progress(const QString &msg); diff --git a/src/plugins/qmldesigner/components/edit3d/bakelightsdatamodel.cpp b/src/plugins/qmldesigner/components/edit3d/bakelightsdatamodel.cpp index 9657d564fb2..95a260c26fd 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 + #include #include #include @@ -140,7 +142,7 @@ bool BakeLightsDataModel::reset() beginResetModel(); m_dataList.clear(); - m_view3dNode = BakeLights::resolveView3dNode(m_view); + m_view3dNode = Utils3D::activeView3dNode(m_view); // Find all models and lights in active View3D QList nodes = m_view3dNode.allSubModelNodes(); diff --git a/src/plugins/qmldesigner/components/edit3d/cameraspeedconfiguration.cpp b/src/plugins/qmldesigner/components/edit3d/cameraspeedconfiguration.cpp new file mode 100644 index 00000000000..76560ac1926 --- /dev/null +++ b/src/plugins/qmldesigner/components/edit3d/cameraspeedconfiguration.cpp @@ -0,0 +1,192 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +#include "cameraspeedconfiguration.h" + +#include "designersettings.h" +#include "edit3dview.h" +#include "edit3dviewconfig.h" + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace QmlDesigner { + +static QString propertyEditorResourcesPath() +{ +#ifdef SHARE_QML_PATH + if (Utils::qtcEnvironmentVariableIsSet("LOAD_QML_FROM_SOURCE")) + return QLatin1String(SHARE_QML_PATH) + "/propertyEditorQmlSources"; +#endif + return Core::ICore::resourcePath("qmldesigner/propertyEditorQmlSources").toString(); +} + +static QString qmlSourcesPath() +{ +#ifdef SHARE_QML_PATH + if (Utils::qtcEnvironmentVariableIsSet("LOAD_QML_FROM_SOURCE")) + return QLatin1String(SHARE_QML_PATH) + "/edit3dQmlSource"; +#endif + return Core::ICore::resourcePath("qmldesigner/edit3dQmlSource").toString(); +} + +CameraSpeedConfiguration::CameraSpeedConfiguration(Edit3DView *view) + : QObject(view) + , m_view(view) +{ +} + +CameraSpeedConfiguration::~CameraSpeedConfiguration() +{ + delete m_configDialog; + restoreCursor(); +} + +void CameraSpeedConfiguration::apply() +{ + if (m_changes && !m_view.isNull()) + m_view->setCameraSpeedAuxData(speed(), multiplier()); + deleteLater(); +} + +void CameraSpeedConfiguration::resetDefaults() +{ + setSpeed(defaultSpeed); + setMultiplier(defaultMultiplier); +} + +void CameraSpeedConfiguration::hideCursor() +{ + if (QGuiApplication::overrideCursor()) + return; + + QGuiApplication::setOverrideCursor(QCursor(Qt::BlankCursor)); + + if (QWindow *w = QGuiApplication::focusWindow()) + m_lastPos = QCursor::pos(w->screen()); +} + +void CameraSpeedConfiguration::restoreCursor() +{ + if (!QGuiApplication::overrideCursor()) + return; + + QGuiApplication::restoreOverrideCursor(); + + if (QWindow *w = QGuiApplication::focusWindow()) + QCursor::setPos(w->screen(), m_lastPos); +} + +void CameraSpeedConfiguration::holdCursorInPlace() +{ + if (!QGuiApplication::overrideCursor()) + return; + + if (QWindow *w = QGuiApplication::focusWindow()) + QCursor::setPos(w->screen(), m_lastPos); +} + +int CameraSpeedConfiguration::devicePixelRatio() +{ + if (QWindow *w = QGuiApplication::focusWindow()) + return w->devicePixelRatio(); + + return 1; +} + +void CameraSpeedConfiguration::showConfigDialog(const QPoint &pos) +{ + double speed, multiplier; + m_view->getCameraSpeedAuxData(speed, multiplier); + + setSpeed(speed); + setMultiplier(multiplier); + + m_changes = false; + + if (!m_configDialog) { + // Show non-modal progress dialog with cancel button + QString path = qmlSourcesPath() + "/CameraSpeedConfigurationDialog.qml"; + + m_configDialog = new QQuickView; + m_configDialog->setResizeMode(QQuickView::SizeViewToRootObject); + m_configDialog->setFlags(Qt::Dialog | Qt::FramelessWindowHint); + m_configDialog->setModality(Qt::NonModal); + m_configDialog->engine()->addImportPath(propertyEditorResourcesPath() + "/imports"); + + m_configDialog->rootContext()->setContextObject(this); + m_configDialog->setSource(QUrl::fromLocalFile(path)); + m_configDialog->installEventFilter(this); + + QPoint finalPos = pos; + finalPos.setX(pos.x() - m_configDialog->size().width() / 2); + finalPos.setY(pos.y()); + m_configDialog->setPosition(finalPos); + } + + m_configDialog->show(); +} + +void CameraSpeedConfiguration::setSpeed(double value) +{ + if (value != m_speed) { + m_speed = value; + m_changes = true; + emit speedChanged(); + emit totalSpeedChanged(); + } +} + +void CameraSpeedConfiguration::setMultiplier(double value) +{ + if (value != m_multiplier) { + m_multiplier = value; + m_changes = true; + emit multiplierChanged(); + emit totalSpeedChanged(); + } +} + +void CameraSpeedConfiguration::asyncClose() +{ + QTimer::singleShot(0, this, [this] { + if (!m_configDialog.isNull() && m_configDialog->isVisible()) + m_configDialog->close(); + }); +} + +void CameraSpeedConfiguration::cancel() +{ + if (!m_configDialog.isNull() && m_configDialog->isVisible()) + m_configDialog->close(); + + deleteLater(); +} + +bool CameraSpeedConfiguration::eventFilter(QObject *obj, QEvent *event) +{ + if (obj == m_configDialog) { + if (event->type() == QEvent::FocusOut) { + asyncClose(); + } else if (event->type() == QEvent::KeyPress) { + QKeyEvent *keyEvent = static_cast(event); + if (keyEvent->key() == Qt::Key_Escape) + asyncClose(); + } else if (event->type() == QEvent::Close) { + apply(); + } + } + + return QObject::eventFilter(obj, event); +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/edit3d/cameraspeedconfiguration.h b/src/plugins/qmldesigner/components/edit3d/cameraspeedconfiguration.h new file mode 100644 index 00000000000..55256890cbb --- /dev/null +++ b/src/plugins/qmldesigner/components/edit3d/cameraspeedconfiguration.h @@ -0,0 +1,76 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 +#pragma once + +#include + +#include +#include +#include + +QT_BEGIN_NAMESPACE +class QQuickView; +QT_END_NAMESPACE + +namespace QmlDesigner { + +class Edit3DView; + +inline constexpr AuxiliaryDataKeyView edit3dCameraSpeedDocProperty{AuxiliaryDataType::Document, + "cameraSpeed3d"}; +inline constexpr AuxiliaryDataKeyView edit3dCameraSpeedMultiplierDocProperty{AuxiliaryDataType::Document, + "cameraSpeed3dMultiplier"}; +inline constexpr AuxiliaryDataKeyView edit3dCameraTotalSpeedProperty{AuxiliaryDataType::NodeInstanceAuxiliary, + "cameraTotalSpeed3d"}; + +class CameraSpeedConfiguration : public QObject +{ + Q_OBJECT + Q_PROPERTY(double speed READ speed WRITE setSpeed NOTIFY speedChanged) + Q_PROPERTY(double multiplier READ multiplier WRITE setMultiplier NOTIFY multiplierChanged) + Q_PROPERTY(double totalSpeed READ totalSpeed NOTIFY totalSpeedChanged) + +public: + CameraSpeedConfiguration(Edit3DView *view); + ~CameraSpeedConfiguration(); + + Q_INVOKABLE void resetDefaults(); + Q_INVOKABLE void hideCursor(); + Q_INVOKABLE void restoreCursor(); + Q_INVOKABLE void holdCursorInPlace(); + Q_INVOKABLE int devicePixelRatio(); + + void cancel(); + void apply(); + + void showConfigDialog(const QPoint &pos); + + void setSpeed(double value); + double speed() const { return m_speed; } + void setMultiplier(double value); + double multiplier() const { return m_multiplier; } + double totalSpeed() const { return m_speed * m_multiplier; } + + constexpr static double defaultSpeed = 25.; + constexpr static double defaultMultiplier = 1.; + +signals: + void speedChanged(); + void multiplierChanged(); + void totalSpeedChanged(); + +protected: + bool eventFilter(QObject *obj, QEvent *event) override; + +private: + void asyncClose(); + + QPointer m_configDialog; + QPointer m_view; + double m_speed = 0.; + double m_multiplier = 0.; + bool m_changes = false; + QPoint m_lastPos; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dactions.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dactions.cpp index add61d195de..b3d140bc887 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dactions.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dactions.cpp @@ -9,6 +9,8 @@ #include "qmldesignerconstants.h" #include "seekerslider.h" +#include + #include namespace QmlDesigner { @@ -149,7 +151,7 @@ bool Edit3DBakeLightsAction::isVisible(const SelectionContext &) const bool Edit3DBakeLightsAction::isEnabled(const SelectionContext &) const { return m_view->isBakingLightsSupported() - && !BakeLights::resolveView3dId(m_view).isEmpty(); + && !Utils3D::activeView3dId(m_view).isEmpty(); } } diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp index fafcff86671..e5ff133c69f 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp @@ -5,6 +5,7 @@ #include "backgroundcolorselection.h" #include "bakelights.h" +#include "cameraspeedconfiguration.h" #include "designeractionmanager.h" #include "designericons.h" #include "designersettings.h" @@ -270,6 +271,8 @@ void Edit3DView::updateActiveScene3D(const QVariantMap &sceneState) selectionContext.setUpdateMode(SelectionContext::UpdateMode::Fast); if (m_bakeLightsAction) m_bakeLightsAction->currentContextChanged(selectionContext); + + syncCameraSpeedToNewView(); } void Edit3DView::modelAttached(Model *model) @@ -764,6 +767,48 @@ void Edit3DView::syncSnapAuxPropsToSettings() Edit3DViewConfig::load(DesignerSettingsKey::EDIT3DVIEW_SNAP_SCALE_INTERVAL)); } +void Edit3DView::setCameraSpeedAuxData(double speed, double multiplier) +{ + ModelNode node = Utils3D::active3DSceneNode(this); + node.setAuxiliaryData(edit3dCameraSpeedDocProperty, speed); + node.setAuxiliaryData(edit3dCameraSpeedMultiplierDocProperty, multiplier); + rootModelNode().setAuxiliaryData(edit3dCameraTotalSpeedProperty, (speed * multiplier)); + m_previousCameraSpeed = speed; + m_previousCameraMultiplier = multiplier; +} + +void Edit3DView::getCameraSpeedAuxData(double &speed, double &multiplier) +{ + ModelNode node = Utils3D::active3DSceneNode(this); + auto speedProp = node.auxiliaryData(edit3dCameraSpeedDocProperty); + auto multProp = node.auxiliaryData(edit3dCameraSpeedMultiplierDocProperty); + speed = speedProp ? speedProp->toDouble() : CameraSpeedConfiguration::defaultSpeed; + multiplier = multProp ? multProp->toDouble() : CameraSpeedConfiguration::defaultMultiplier; +} + +void Edit3DView::syncCameraSpeedToNewView() +{ + // Camera speed is inherited from previous active view if explicit values have not been + // stored for the currently active view + ModelNode node = Utils3D::active3DSceneNode(this); + auto speedProp = node.auxiliaryData(edit3dCameraSpeedDocProperty); + auto multProp = node.auxiliaryData(edit3dCameraSpeedMultiplierDocProperty); + double speed = CameraSpeedConfiguration::defaultSpeed; + double multiplier = CameraSpeedConfiguration::defaultMultiplier; + + if (!speedProp || !multProp) { + if (m_previousCameraSpeed > 0 && m_previousCameraMultiplier > 0) { + speed = m_previousCameraSpeed; + multiplier = m_previousCameraMultiplier; + } + } else { + speed = speedProp->toDouble(); + multiplier = multProp->toDouble(); + } + + setCameraSpeedAuxData(speed, multiplier); +} + const QList &Edit3DView::splitToolStates() const { return m_splitToolStates; @@ -1156,6 +1201,30 @@ void Edit3DView::createEdit3DActions() toolbarIcon(DesignerIcons::SplitViewIcon), this); + SelectionContextOperation cameraSpeedConfigTrigger = [this](const SelectionContext &) { + if (!m_cameraSpeedConfiguration) { + m_cameraSpeedConfiguration = new CameraSpeedConfiguration(this); + connect(m_cameraSpeedConfiguration.data(), &CameraSpeedConfiguration::totalSpeedChanged, + this, [this] { + setCameraSpeedAuxData(m_cameraSpeedConfiguration->speed(), + m_cameraSpeedConfiguration->multiplier()); + }); + } + m_cameraSpeedConfiguration->showConfigDialog(resolveToolbarPopupPos(m_cameraSpeedConfigAction.get())); + }; + + m_cameraSpeedConfigAction = std::make_unique( + QmlDesigner::Constants::EDIT3D_CAMERA_SPEED_CONFIG, + View3DActionType::Empty, + QCoreApplication::translate("CameraSpeedConfigAction", "Open camera speed configuration dialog"), + QKeySequence(), + false, + false, + toolbarIcon(DesignerIcons::SnappingConfIcon), // TODO proper icon + this, + cameraSpeedConfigTrigger); + + m_leftActions << m_selectionModeAction.get(); m_leftActions << nullptr; // Null indicates separator m_leftActions << nullptr; // Second null after separator indicates an exclusive group @@ -1174,6 +1243,7 @@ void Edit3DView::createEdit3DActions() m_leftActions << nullptr; m_leftActions << m_alignCamerasAction.get(); m_leftActions << m_alignViewAction.get(); + m_leftActions << m_cameraSpeedConfigAction.get(); m_leftActions << nullptr; m_leftActions << m_visibilityTogglesAction.get(); m_leftActions << m_backgroundColorMenuAction.get(); diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.h b/src/plugins/qmldesigner/components/edit3d/edit3dview.h index 63b815f392b..781b26d8d8d 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.h +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.h @@ -25,6 +25,7 @@ QT_END_NAMESPACE namespace QmlDesigner { class BakeLights; +class CameraSpeedConfiguration; class Edit3DWidget; class SnapConfiguration; @@ -85,6 +86,8 @@ public: bool isBakingLightsSupported() const; void syncSnapAuxPropsToSettings(); + void setCameraSpeedAuxData(double speed, double multiplier); + void getCameraSpeedAuxData(double &speed, double &multiplier); const QList &splitToolStates() const; void setSplitToolState(int splitIndex, const SplitToolState &state); @@ -123,6 +126,7 @@ private: void createResetColorAction(QAction *syncEnvBackgroundAction); void createSyncEnvBackgroundAction(); void createSeekerSliderAction(); + void syncCameraSpeedToNewView(); QPoint resolveToolbarPopupPos(Edit3DAction *action) const; @@ -164,6 +168,7 @@ private: std::unique_ptr m_backgroundColorMenuAction; std::unique_ptr m_snapToggleAction; std::unique_ptr m_snapConfigAction; + std::unique_ptr m_cameraSpeedConfigAction; std::unique_ptr m_bakeLightsAction; int particlemode; @@ -178,12 +183,16 @@ private: QPointer m_bakeLights; bool m_isBakingLightsSupported = false; QPointer m_snapConfiguration; + QPointer m_cameraSpeedConfiguration; int m_activeSplit = 0; QList m_splitToolStates; QList m_flyModeDisabledActions; ModelNode m_contextMenuPendingNode; + double m_previousCameraSpeed = -1.; + double m_previousCameraMultiplier = -1.; + friend class Edit3DAction; }; diff --git a/src/plugins/qmldesigner/qmldesignerconstants.h b/src/plugins/qmldesigner/qmldesignerconstants.h index 547d42e6e99..db63d894fec 100644 --- a/src/plugins/qmldesigner/qmldesignerconstants.h +++ b/src/plugins/qmldesigner/qmldesignerconstants.h @@ -75,6 +75,7 @@ inline constexpr char EDIT3D_BACKGROUND_COLOR_ACTIONS[] inline constexpr char EDIT3D_BAKE_LIGHTS[] = "QmlDesigner.Editor3D.BakeLights"; inline constexpr char EDIT3D_SNAP_TOGGLE[] = "QmlDesigner.Editor3D.SnapToggle"; 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"; diff --git a/src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml b/src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml index e2321dbc3ac..446d5756838 100644 --- a/src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml +++ b/src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml @@ -26,7 +26,7 @@ Item { readonly property vector3d _defaultCameraPosition: Qt.vector3d(0, 600, 600) readonly property vector3d _defaultCameraRotation: Qt.vector3d(-45, 0, 0) readonly property real _defaultCameraLookAtDistance: _defaultCameraPosition.length() - readonly property real _keyPanAmount: 10 + readonly property real _keyPanAmount: _generalHelper.cameraSpeed property bool ignoreToolState: false property bool flyMode: viewRoot.flyMode diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp index 52070332b46..6886f7e41c5 100644 --- a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp +++ b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp @@ -1116,6 +1116,14 @@ void GeneralHelper::setSnapPositionInterval(double interval) } } +void GeneralHelper::setCameraSpeed(double speed) +{ + if (m_cameraSpeed != speed) { + m_cameraSpeed = speed; + emit cameraSpeedChanged(); + } +} + QString GeneralHelper::formatVectorDragTooltip(const QVector3D &vec, const QString &suffix) const { return QObject::tr("x:%L1 y:%L2 z:%L3%L4") diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h index 9afaf077c9b..3ba6f8b1d9f 100644 --- a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h +++ b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h @@ -38,6 +38,7 @@ class GeneralHelper : public QObject Q_PROPERTY(bool isMacOS READ isMacOS CONSTANT) Q_PROPERTY(QVariant bgColor READ bgColor NOTIFY bgColorChanged FINAL) Q_PROPERTY(double minGridStep READ minGridStep NOTIFY minGridStepChanged FINAL) + Q_PROPERTY(double cameraSpeed READ cameraSpeed NOTIFY cameraSpeedChanged FINAL) public: GeneralHelper(); @@ -135,12 +136,14 @@ public: void setSnapPositionInterval(double interval); void setSnapRotationInterval(double interval) { m_snapRotationInterval = interval; } void setSnapScaleInterval(double interval) { m_snapScaleInterval = interval / 100.; } + void setCameraSpeed(double speed); Q_INVOKABLE QString snapPositionDragTooltip(const QVector3D &pos) const; Q_INVOKABLE QString snapRotationDragTooltip(double angle) const; Q_INVOKABLE QString snapScaleDragTooltip(const QVector3D &scale) const; double minGridStep() const; + double cameraSpeed() const { return m_cameraSpeed; } void setBgColor(const QVariant &colors); QVariant bgColor() const { return m_bgColor; } @@ -161,6 +164,7 @@ signals: void sceneEnvDataChanged(); void requestCameraMove(QQuick3DCamera *camera, const QVector3D &moveVector); void requestRender(); + void cameraSpeedChanged(); private: void handlePendingToolStateUpdate(); @@ -214,6 +218,7 @@ private: double m_snapPositionInterval = 50.; double m_snapRotationInterval = 5.; double m_snapScaleInterval = .1; + double m_cameraSpeed = 10.; QVariant m_bgColor; }; diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp index 1ce4e0364c8..c7cab45ed4d 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp @@ -343,7 +343,7 @@ void Qt5InformationNodeInstanceServer::updateRotationBlocks( #endif } -void Qt5InformationNodeInstanceServer::updateSnapSettings( +void Qt5InformationNodeInstanceServer::updateSnapAndCameraSettings( [[maybe_unused]] const QVector &valueChanges) { #ifdef QUICK3D_MODULE @@ -372,6 +372,8 @@ void Qt5InformationNodeInstanceServer::updateSnapSettings( } else if (container.name() == "snapAbs3d") { helper->setSnapAbsolute(container.value().toBool()); changed = true; + } else if (container.name() == "cameraTotalSpeed3d") { + helper->setCameraSpeed(container.value().toDouble()); } } if (changed) @@ -2103,7 +2105,7 @@ void Qt5InformationNodeInstanceServer::createScene(const CreateSceneCommand &com setup3DEditView(instanceList, command); updateRotationBlocks(command.auxiliaryChanges); updateMaterialPreviewData(command.auxiliaryChanges); - updateSnapSettings(command.auxiliaryChanges); + updateSnapAndCameraSettings(command.auxiliaryChanges); updateColorSettings(command.auxiliaryChanges); } @@ -2613,7 +2615,7 @@ void Qt5InformationNodeInstanceServer::changeAuxiliaryValues(const ChangeAuxilia { updateRotationBlocks(command.auxiliaryChanges); updateMaterialPreviewData(command.auxiliaryChanges); - updateSnapSettings(command.auxiliaryChanges); + updateSnapAndCameraSettings(command.auxiliaryChanges); updateColorSettings(command.auxiliaryChanges); Qt5NodeInstanceServer::changeAuxiliaryValues(command); render3DEditView(); diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.h b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.h index d18266dd589..4f7fcd71774 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.h +++ b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.h @@ -124,7 +124,7 @@ private: void resolveImportSupport(); void updateMaterialPreviewData(const QVector &valueChanges); void updateRotationBlocks(const QVector &valueChanges); - void updateSnapSettings(const QVector &valueChanges); + void updateSnapAndCameraSettings(const QVector &valueChanges); void updateColorSettings(const QVector &valueChanges); void removeRotationBlocks(const QVector &instanceIds); void getNodeAtPos(const QPointF &pos); From b6318addef176606a9387d6b198cee05a815082b Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Thu, 14 Mar 2024 11:59:48 +0200 Subject: [PATCH 172/176] QmlDesigner: Fix right click selection on 3D view Right click on 3D view will now always clear selection if there is no model under the cursor when click happens. Fixes: QDS-12227 Change-Id: I922fc79472ced4a3aa5e518c03731edf240e1d43 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Mahmoud Badri --- src/plugins/qmldesigner/components/edit3d/edit3dview.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp index e5ff133c69f..6073f565b61 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp @@ -705,8 +705,13 @@ void Edit3DView::showContextMenu() if (m_nodeAtPosReqType == NodeAtPosReqType::ContextMenu) return; - if (m_contextMenuPendingNode.isValid() && !m_contextMenuPendingNode.isSelected()) - setSelectedModelNode(m_contextMenuPendingNode); + if (m_contextMenuPendingNode.isValid()) { + if (!m_contextMenuPendingNode.isSelected()) + setSelectedModelNode(m_contextMenuPendingNode); + } else { + clearSelectedModelNodes(); + } + m_edit3DWidget->showContextMenu(m_contextMenuPosMouse, m_contextMenuPendingNode, m_contextMenuPos3D); m_contextMenuPendingNode = {}; } From c581576e27ea13ec25ab8470b5a9254d8daebb19 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Wed, 13 Mar 2024 14:33:08 +0200 Subject: [PATCH 173/176] QmlDesigner: Use flags instead of int in FlagsComboBox Fixes: QDS-12217 Change-Id: I0b87e084ac35ade002ed4d4f85ee141bd9e527b8 Reviewed-by: Miikka Heikkinen --- .../imports/HelperWidgets/FlagsComboBox.qml | 60 ++++++++++++------- 1 file changed, 40 insertions(+), 20 deletions(-) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FlagsComboBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FlagsComboBox.qml index 8bc9ad44282..46c98f25c50 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FlagsComboBox.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FlagsComboBox.qml @@ -12,11 +12,11 @@ import StudioTheme as StudioTheme itemsModel: ListModel { ListElement { name: "..." - flag: ... + flag: "..." } ListElement { name: "..." - flag: ... + flag: "..." } ... */ @@ -29,30 +29,36 @@ StudioControls.CustomComboBox { property bool showExtendedFunctionButton: true + property string scope: "Qt" // flag prefix scope + property string zeroFlag + Connections { id: backendValueConnection target: backendValue function onValueChangedQml() { - let numSelected = 0 let selectedItem = "" + let flags = root.backendValue.expression.split(/\s*\|\s*/).filter(Boolean) + for (let i = 0; i < root.itemsModel.count; ++i) { - let flag = root.itemsModel.get(i).flag - let flagActive = root.backendValue.value & flag + let flag = root.scope + "." + root.itemsModel.get(i).flag + let flagActive = flags.indexOf(flag) !== -1 root.popupItem.itemAt(i).checked = flagActive - if (flagActive) { + if (flagActive) selectedItem = root.itemsModel.get(i).name - ++numSelected - } } // update ComboBox text - root.model = numSelected == 0 ? [qsTr("empty")] - : numSelected == 1 ? root.model = [selectedItem] - : [qsTr("%1 items selected").arg(numSelected)] + let numSelected = flags.length + if (flags.length > 0 && flags[0] === root.scope + "." + root.zeroFlag) + --numSelected + + root.model = numSelected === 0 ? [qsTr("empty")] + : numSelected === 1 ? root.model = [selectedItem] + : [qsTr("%1 items selected").arg(numSelected)] } } @@ -76,18 +82,23 @@ StudioControls.CustomComboBox { width: 80 onClicked: { - let allFlags = 0 - for (let i = 0; i < root.itemsModel.count; ++i) - allFlags += root.itemsModel.get(i).flag + let allFlags = root.scope + "." + root.itemsModel.get(0).flag + for (let i = 1; i < root.itemsModel.count; ++i) + allFlags += " | " + root.scope + "." + root.itemsModel.get(i).flag - root.backendValue.value = allFlags + root.backendValue.expression = allFlags } } HelperWidgets.Button { text: qsTr("Select None") width: 80 - onClicked: root.backendValue.value = 0 + onClicked: { + if (root.zeroFlag) + root.backendValue.expression = root.scope + "." + root.zeroFlag + else + root.backendValue.resetValue() + } } } @@ -100,10 +111,19 @@ StudioControls.CustomComboBox { actionIndicatorVisible: false onToggled: { - if (root.popupItem.itemAt(index).checked) - root.backendValue.value |= flag - else - root.backendValue.value &= ~flag + let flags = root.backendValue.expression.split(/\s*\|\s*/).filter(Boolean) + let scopedFlag = root.scope + "." + flag + let idx = flags.indexOf(scopedFlag) + + if (root.popupItem.itemAt(index).checked) { + if (idx === -1) + flags.push(scopedFlag) + } else { + if (idx !== -1) + flags.splice(idx, 1) + } + + root.backendValue.expression = flags.join(" | ") } } } From a43b3e916c80ed4c5197c617cc96eba92367dc45 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Thu, 14 Mar 2024 12:59:08 +0200 Subject: [PATCH 174/176] QmlDesigner: Use the trimmed value for urls Fixes: QDS-12213 Change-Id: Id177a897849335e4cddca1af12ceca682c3f531f Reviewed-by: Mahmoud Badri --- .../components/collectioneditor/collectiondetails.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp index 80e223d2ec9..9005882813d 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp @@ -86,7 +86,7 @@ static bool getCustomUrl(const QString &value, "){1}$" // end of Address }; - const QRegularExpressionMatch match = urlRegex.match(value); + const QRegularExpressionMatch match = urlRegex.match(value.trimmed()); if (match.hasMatch()) { if (match.hasCaptured("Address")) { if (match.hasCaptured("MimeType") && match.captured("MainType") == "image") @@ -162,7 +162,7 @@ static CollectionDetails::DataType dataTypeFromString(const QString &value) return DataType::Real; DataType urlType; - if (getCustomUrl(value, urlType)) + if (getCustomUrl(trimmedValue, urlType)) return urlType; return DataType::String; From 2547e377aeea9341ba6d2199f84b8c1dc1798eb5 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Thu, 14 Mar 2024 14:19:16 +0200 Subject: [PATCH 175/176] QmlDesigner: Add camera icon and update camera align icons Change-Id: I96dbdf39a19642805485f135a101a5464bb4cbe8 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Miikka Heikkinen --- .../imports/StudioTheme/InternalConstants.qml | 605 +++++++++--------- .../imports/StudioTheme/icons.ttf | Bin 66308 -> 66460 bytes .../components/componentcore/theme.h | 1 + 3 files changed, 304 insertions(+), 302 deletions(-) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml index 92d0d771a3c..ee2df02e04a 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml @@ -75,308 +75,309 @@ QtObject { readonly property string binding_medium: "\u005C" readonly property string bounds_small: "\u005D" readonly property string branch_medium: "\u005E" - readonly property string camera_small: "\u005F" - readonly property string centerHorizontal: "\u0060" - readonly property string centerVertical: "\u0061" - readonly property string cleanLogs_medium: "\u0062" - readonly property string clearList_large: "\u0063" - readonly property string clearList_medium: "\u0064" - readonly property string closeCross: "\u0065" - readonly property string closeFile_large: "\u0066" - readonly property string closeLink: "\u0067" - readonly property string close_small: "\u0068" - readonly property string code: "\u0069" - readonly property string codeEditor_medium: "\u006A" - readonly property string codeview_medium: "\u006B" - readonly property string colorPopupClose: "\u006C" - readonly property string colorSelection_medium: "\u006D" - readonly property string columnsAndRows: "\u006E" - readonly property string comboBox_medium: "\u006F" - readonly property string cone_medium: "\u0070" - readonly property string cone_small: "\u0071" - readonly property string connection_small: "\u0072" - readonly property string connections_medium: "\u0073" - readonly property string copyLink: "\u0074" - readonly property string copyStyle: "\u0075" - readonly property string copy_small: "\u0076" - readonly property string cornerA: "\u0077" - readonly property string cornerB: "\u0078" - readonly property string cornersAll: "\u0079" - readonly property string createComponent_large: "\u007A" - readonly property string createComponent_small: "\u007B" - readonly property string createObject_medium: "\u007C" - readonly property string create_medium: "\u007D" - readonly property string create_small: "\u007E" - readonly property string cube_medium: "\u007F" - readonly property string cube_small: "\u0080" - readonly property string curveDesigner: "\u0081" - readonly property string curveDesigner_medium: "\u0082" - readonly property string curveEditor: "\u0083" - readonly property string customMaterialEditor: "\u0084" - readonly property string cylinder_medium: "\u0085" - readonly property string cylinder_small: "\u0086" - readonly property string decisionNode: "\u0087" - readonly property string deleteColumn: "\u0088" - readonly property string deleteMaterial: "\u0089" - readonly property string deleteRow: "\u008A" - readonly property string deleteTable: "\u008B" - readonly property string delete_medium: "\u008C" - readonly property string delete_small: "\u008D" - readonly property string deletecolumn_medium: "\u008E" - readonly property string deletepermanently_medium: "\u008F" - readonly property string deleterow_medium: "\u0090" - readonly property string designMode_large: "\u0091" - readonly property string detach: "\u0092" - readonly property string directionalLight_small: "\u0093" - readonly property string distributeBottom: "\u0094" - readonly property string distributeCenterHorizontal: "\u0095" - readonly property string distributeCenterVertical: "\u0096" - readonly property string distributeLeft: "\u0097" - readonly property string distributeOriginBottomRight: "\u0098" - readonly property string distributeOriginCenter: "\u0099" - readonly property string distributeOriginNone: "\u009A" - readonly property string distributeOriginTopLeft: "\u009B" - readonly property string distributeRight: "\u009D" - readonly property string distributeSpacingHorizontal: "\u009E" - readonly property string distributeSpacingVertical: "\u009F" - readonly property string distributeTop: "\u00A0" - readonly property string download: "\u00A1" - readonly property string downloadUnavailable: "\u00A2" - readonly property string downloadUpdate: "\u00A3" - readonly property string downloaded: "\u00A4" - readonly property string dragmarks: "\u00A5" - readonly property string duplicate_small: "\u00A6" - readonly property string edit: "\u00A7" - readonly property string editComponent_large: "\u00A8" - readonly property string editComponent_small: "\u00A9" - readonly property string editLightOff_medium: "\u00AA" - readonly property string editLightOn_medium: "\u00AB" - readonly property string edit_medium: "\u00AC" - readonly property string edit_small: "\u00AE" - readonly property string effects: "\u00AF" - readonly property string events_small: "\u00B0" - readonly property string export_medium: "\u00B1" - readonly property string eyeDropper: "\u00B2" - readonly property string favorite: "\u00B3" - readonly property string fitAll_medium: "\u00B4" - readonly property string fitSelected_small: "\u00B5" - readonly property string fitSelection_medium: "\u00B6" - readonly property string fitToView_medium: "\u00B7" - readonly property string flowAction: "\u00B8" - readonly property string flowTransition: "\u00B9" - readonly property string fontStyleBold: "\u00BA" - readonly property string fontStyleItalic: "\u00BB" - readonly property string fontStyleStrikethrough: "\u00BC" - readonly property string fontStyleUnderline: "\u00BD" - readonly property string forward_medium: "\u00BE" - readonly property string globalOrient_medium: "\u00BF" - readonly property string gradient: "\u00C0" - readonly property string gridView: "\u00C1" - readonly property string grid_medium: "\u00C2" - readonly property string group_small: "\u00C3" - readonly property string help: "\u00C4" - readonly property string home_large: "\u00C5" - readonly property string idAliasOff: "\u00C6" - readonly property string idAliasOn: "\u00C7" - readonly property string import_medium: "\u00C8" - readonly property string imported: "\u00C9" - readonly property string importedModels_small: "\u00CA" - readonly property string infinity: "\u00CB" - readonly property string invisible_medium: "\u00CC" - readonly property string invisible_small: "\u00CD" - readonly property string jumpToCode_medium: "\u00CE" - readonly property string jumpToCode_small: "\u00CF" - readonly property string keyframe: "\u00D0" - readonly property string languageList_medium: "\u00D1" - readonly property string layouts_small: "\u00D2" - readonly property string lights_small: "\u00D3" - readonly property string linear_medium: "\u00D4" - readonly property string linkTriangle: "\u00D5" - readonly property string linked: "\u00D6" - readonly property string listView: "\u00D7" - readonly property string listView_medium: "\u00D8" - readonly property string list_medium: "\u00D9" - readonly property string localOrient_medium: "\u00DA" - readonly property string lockOff: "\u00DB" - readonly property string lockOn: "\u00DC" - readonly property string loopPlayback_medium: "\u00DD" - readonly property string materialBrowser_medium: "\u00DE" - readonly property string materialPreviewEnvironment: "\u00DF" - readonly property string materialPreviewModel: "\u00E0" - readonly property string material_medium: "\u00E1" - readonly property string maxBar_small: "\u00E2" - readonly property string mergeCells: "\u00E3" - readonly property string merge_small: "\u00E4" - readonly property string minus: "\u00E5" - readonly property string mirror: "\u00E6" - readonly property string more_medium: "\u00E7" - readonly property string mouseArea_small: "\u00E8" - readonly property string moveDown_medium: "\u00E9" - readonly property string moveInwards_medium: "\u00EA" - readonly property string moveUp_medium: "\u00EB" - readonly property string moveUpwards_medium: "\u00EC" - readonly property string move_medium: "\u00ED" - readonly property string newMaterial: "\u00EE" - readonly property string nextFile_large: "\u00EF" - readonly property string normalBar_small: "\u00F0" - readonly property string openLink: "\u00F1" - readonly property string openMaterialBrowser: "\u00F2" - readonly property string orientation: "\u00F3" - readonly property string orthCam_medium: "\u00F4" - readonly property string orthCam_small: "\u00F5" - readonly property string paddingEdge: "\u00F6" - readonly property string paddingFrame: "\u00F7" - readonly property string particleAnimation_medium: "\u00F8" - readonly property string pasteStyle: "\u00F9" - readonly property string paste_small: "\u00FA" - readonly property string pause: "\u00FB" - readonly property string pause_medium: "\u00FC" - readonly property string perspectiveCam_medium: "\u00FD" - readonly property string perspectiveCam_small: "\u00FE" - readonly property string pin: "\u00FF" - readonly property string plane_medium: "\u0100" - readonly property string plane_small: "\u0101" - readonly property string play: "\u0102" - readonly property string playFill_medium: "\u0103" - readonly property string playOutline_medium: "\u0104" - readonly property string plus: "\u0105" - readonly property string pointLight_small: "\u0106" - readonly property string positioners_small: "\u0107" - readonly property string previewEnv_medium: "\u0108" - readonly property string previousFile_large: "\u0109" - readonly property string promote: "\u010A" - readonly property string properties_medium: "\u010B" - readonly property string readOnly: "\u010C" - readonly property string recent_medium: "\u010D" - readonly property string recordFill_medium: "\u010E" - readonly property string recordOutline_medium: "\u010F" - readonly property string redo: "\u0110" - readonly property string reload_medium: "\u0111" - readonly property string remove_medium: "\u0112" - readonly property string remove_small: "\u0113" - readonly property string rename_small: "\u0114" - readonly property string replace_small: "\u0115" - readonly property string resetView_small: "\u0116" - readonly property string restartParticles_medium: "\u0117" - readonly property string reverseOrder_medium: "\u0118" - readonly property string roatate_medium: "\u0119" - readonly property string rotationFill: "\u011A" - readonly property string rotationOutline: "\u011B" - readonly property string runProjFill_large: "\u011C" - readonly property string runProjOutline_large: "\u011D" - readonly property string s_anchors: "\u011E" - readonly property string s_annotations: "\u011F" - readonly property string s_arrange: "\u0120" - readonly property string s_boundingBox: "\u0121" - readonly property string s_component: "\u0122" - readonly property string s_connections: "\u0123" - readonly property string s_edit: "\u0124" - readonly property string s_enterComponent: "\u0125" - readonly property string s_eventList: "\u0126" - readonly property string s_group: "\u0127" - readonly property string s_layouts: "\u0128" - readonly property string s_merging: "\u0129" - readonly property string s_mouseArea: "\u012A" - readonly property string s_positioners: "\u012B" - readonly property string s_selection: "\u012C" - readonly property string s_snapping: "\u012D" - readonly property string s_timeline: "\u012E" - readonly property string s_visibility: "\u012F" - readonly property string saveAs_medium: "\u0130" - readonly property string saveLogs_medium: "\u0131" - readonly property string save_medium: "\u0132" - readonly property string scale_medium: "\u0133" - readonly property string search: "\u0134" - readonly property string search_small: "\u0135" - readonly property string sectionToggle: "\u0136" - readonly property string selectFill_medium: "\u0137" - readonly property string selectOutline_medium: "\u0138" - readonly property string selectParent_small: "\u0139" - readonly property string selection_small: "\u013A" - readonly property string settings_medium: "\u013B" - readonly property string signal_small: "\u013C" - readonly property string snapping_conf_medium: "\u013D" - readonly property string snapping_medium: "\u013E" - readonly property string snapping_small: "\u013F" - readonly property string sortascending_medium: "\u0140" - readonly property string sortdescending_medium: "\u0141" - readonly property string sphere_medium: "\u0142" - readonly property string sphere_small: "\u0143" - readonly property string splitColumns: "\u0144" - readonly property string splitRows: "\u0145" - readonly property string splitScreen_medium: "\u0146" - readonly property string spotLight_small: "\u0147" - readonly property string stackedContainer_small: "\u0148" - readonly property string startNode: "\u0149" - readonly property string step_medium: "\u014A" - readonly property string stop_medium: "\u014B" - readonly property string tableView_medium: "\u014C" - readonly property string testIcon: "\u014D" - readonly property string textAlignBottom: "\u014E" - readonly property string textAlignCenter: "\u014F" - readonly property string textAlignJustified: "\u0150" - readonly property string textAlignLeft: "\u0151" - readonly property string textAlignMiddle: "\u0152" - readonly property string textAlignRight: "\u0153" - readonly property string textAlignTop: "\u0154" - readonly property string textBulletList: "\u0155" - readonly property string textFullJustification: "\u0156" - readonly property string textNumberedList: "\u0157" - readonly property string textures_medium: "\u0158" - readonly property string tickIcon: "\u0159" - readonly property string tickMark_small: "\u015A" - readonly property string timeline_small: "\u015B" - readonly property string toEndFrame_medium: "\u015C" - readonly property string toNextFrame_medium: "\u015D" - readonly property string toPrevFrame_medium: "\u015E" - readonly property string toStartFrame_medium: "\u015F" - readonly property string topToolbar_annotations: "\u0160" - readonly property string topToolbar_closeFile: "\u0161" - readonly property string topToolbar_designMode: "\u0162" - readonly property string topToolbar_enterComponent: "\u0163" - readonly property string topToolbar_home: "\u0164" - readonly property string topToolbar_makeComponent: "\u0165" - readonly property string topToolbar_navFile: "\u0166" - readonly property string topToolbar_runProject: "\u0167" - readonly property string translationCreateFiles: "\u0168" - readonly property string translationCreateReport: "\u0169" - readonly property string translationExport: "\u016A" - readonly property string translationImport: "\u016B" - readonly property string translationSelectLanguages: "\u016C" - readonly property string translationTest: "\u016D" - readonly property string transparent: "\u016E" - readonly property string triState: "\u016F" - readonly property string triangleArcA: "\u0170" - readonly property string triangleArcB: "\u0171" - readonly property string triangleCornerA: "\u0172" - readonly property string triangleCornerB: "\u0173" - readonly property string unLinked: "\u0174" - readonly property string undo: "\u0175" - readonly property string unify_medium: "\u0176" - readonly property string unpin: "\u0177" - readonly property string upDownIcon: "\u0178" - readonly property string upDownSquare2: "\u0179" - readonly property string updateAvailable_medium: "\u017A" - readonly property string updateContent_medium: "\u017B" - readonly property string visibilityOff: "\u017C" - readonly property string visibilityOn: "\u017D" - readonly property string visible_medium: "\u017E" - readonly property string visible_small: "\u017F" - readonly property string warning_medium: "\u0180" - readonly property string wildcard: "\u0181" - readonly property string wizardsAutomotive: "\u0182" - readonly property string wizardsDesktop: "\u0183" - readonly property string wizardsGeneric: "\u0184" - readonly property string wizardsMcuEmpty: "\u0185" - readonly property string wizardsMcuGraph: "\u0186" - readonly property string wizardsMobile: "\u0187" - readonly property string wizardsUnknown: "\u0188" - readonly property string zoomAll: "\u0189" - readonly property string zoomIn: "\u018A" - readonly property string zoomIn_medium: "\u018B" - readonly property string zoomOut: "\u018C" - readonly property string zoomOut_medium: "\u018D" - readonly property string zoomSelection: "\u018E" + readonly property string camera_medium: "\u005F" + readonly property string camera_small: "\u0060" + readonly property string centerHorizontal: "\u0061" + readonly property string centerVertical: "\u0062" + readonly property string cleanLogs_medium: "\u0063" + readonly property string clearList_large: "\u0064" + readonly property string clearList_medium: "\u0065" + readonly property string closeCross: "\u0066" + readonly property string closeFile_large: "\u0067" + readonly property string closeLink: "\u0068" + readonly property string close_small: "\u0069" + readonly property string code: "\u006A" + readonly property string codeEditor_medium: "\u006B" + readonly property string codeview_medium: "\u006C" + readonly property string colorPopupClose: "\u006D" + readonly property string colorSelection_medium: "\u006E" + readonly property string columnsAndRows: "\u006F" + readonly property string comboBox_medium: "\u0070" + readonly property string cone_medium: "\u0071" + readonly property string cone_small: "\u0072" + readonly property string connection_small: "\u0073" + readonly property string connections_medium: "\u0074" + readonly property string copyLink: "\u0075" + readonly property string copyStyle: "\u0076" + readonly property string copy_small: "\u0077" + readonly property string cornerA: "\u0078" + readonly property string cornerB: "\u0079" + readonly property string cornersAll: "\u007A" + readonly property string createComponent_large: "\u007B" + readonly property string createComponent_small: "\u007C" + readonly property string createObject_medium: "\u007D" + readonly property string create_medium: "\u007E" + readonly property string create_small: "\u007F" + readonly property string cube_medium: "\u0080" + readonly property string cube_small: "\u0081" + readonly property string curveDesigner: "\u0082" + readonly property string curveDesigner_medium: "\u0083" + readonly property string curveEditor: "\u0084" + readonly property string customMaterialEditor: "\u0085" + readonly property string cylinder_medium: "\u0086" + readonly property string cylinder_small: "\u0087" + readonly property string decisionNode: "\u0088" + readonly property string deleteColumn: "\u0089" + readonly property string deleteMaterial: "\u008A" + readonly property string deleteRow: "\u008B" + readonly property string deleteTable: "\u008C" + readonly property string delete_medium: "\u008D" + readonly property string delete_small: "\u008E" + readonly property string deletecolumn_medium: "\u008F" + readonly property string deletepermanently_medium: "\u0090" + readonly property string deleterow_medium: "\u0091" + readonly property string designMode_large: "\u0092" + readonly property string detach: "\u0093" + readonly property string directionalLight_small: "\u0094" + readonly property string distributeBottom: "\u0095" + readonly property string distributeCenterHorizontal: "\u0096" + readonly property string distributeCenterVertical: "\u0097" + readonly property string distributeLeft: "\u0098" + readonly property string distributeOriginBottomRight: "\u0099" + readonly property string distributeOriginCenter: "\u009A" + readonly property string distributeOriginNone: "\u009B" + readonly property string distributeOriginTopLeft: "\u009D" + readonly property string distributeRight: "\u009E" + readonly property string distributeSpacingHorizontal: "\u009F" + readonly property string distributeSpacingVertical: "\u00A0" + readonly property string distributeTop: "\u00A1" + readonly property string download: "\u00A2" + readonly property string downloadUnavailable: "\u00A3" + readonly property string downloadUpdate: "\u00A4" + readonly property string downloaded: "\u00A5" + readonly property string dragmarks: "\u00A6" + readonly property string duplicate_small: "\u00A7" + readonly property string edit: "\u00A8" + readonly property string editComponent_large: "\u00A9" + readonly property string editComponent_small: "\u00AA" + readonly property string editLightOff_medium: "\u00AB" + readonly property string editLightOn_medium: "\u00AC" + readonly property string edit_medium: "\u00AE" + readonly property string edit_small: "\u00AF" + readonly property string effects: "\u00B0" + readonly property string events_small: "\u00B1" + readonly property string export_medium: "\u00B2" + readonly property string eyeDropper: "\u00B3" + readonly property string favorite: "\u00B4" + readonly property string fitAll_medium: "\u00B5" + readonly property string fitSelected_small: "\u00B6" + readonly property string fitSelection_medium: "\u00B7" + readonly property string fitToView_medium: "\u00B8" + readonly property string flowAction: "\u00B9" + readonly property string flowTransition: "\u00BA" + readonly property string fontStyleBold: "\u00BB" + readonly property string fontStyleItalic: "\u00BC" + readonly property string fontStyleStrikethrough: "\u00BD" + readonly property string fontStyleUnderline: "\u00BE" + readonly property string forward_medium: "\u00BF" + readonly property string globalOrient_medium: "\u00C0" + readonly property string gradient: "\u00C1" + readonly property string gridView: "\u00C2" + readonly property string grid_medium: "\u00C3" + readonly property string group_small: "\u00C4" + readonly property string help: "\u00C5" + readonly property string home_large: "\u00C6" + readonly property string idAliasOff: "\u00C7" + readonly property string idAliasOn: "\u00C8" + readonly property string import_medium: "\u00C9" + readonly property string imported: "\u00CA" + readonly property string importedModels_small: "\u00CB" + readonly property string infinity: "\u00CC" + readonly property string invisible_medium: "\u00CD" + readonly property string invisible_small: "\u00CE" + readonly property string jumpToCode_medium: "\u00CF" + readonly property string jumpToCode_small: "\u00D0" + readonly property string keyframe: "\u00D1" + readonly property string languageList_medium: "\u00D2" + readonly property string layouts_small: "\u00D3" + readonly property string lights_small: "\u00D4" + readonly property string linear_medium: "\u00D5" + readonly property string linkTriangle: "\u00D6" + readonly property string linked: "\u00D7" + readonly property string listView: "\u00D8" + readonly property string listView_medium: "\u00D9" + readonly property string list_medium: "\u00DA" + readonly property string localOrient_medium: "\u00DB" + readonly property string lockOff: "\u00DC" + readonly property string lockOn: "\u00DD" + readonly property string loopPlayback_medium: "\u00DE" + readonly property string materialBrowser_medium: "\u00DF" + readonly property string materialPreviewEnvironment: "\u00E0" + readonly property string materialPreviewModel: "\u00E1" + readonly property string material_medium: "\u00E2" + readonly property string maxBar_small: "\u00E3" + readonly property string mergeCells: "\u00E4" + readonly property string merge_small: "\u00E5" + readonly property string minus: "\u00E6" + readonly property string mirror: "\u00E7" + readonly property string more_medium: "\u00E8" + readonly property string mouseArea_small: "\u00E9" + readonly property string moveDown_medium: "\u00EA" + readonly property string moveInwards_medium: "\u00EB" + readonly property string moveUp_medium: "\u00EC" + readonly property string moveUpwards_medium: "\u00ED" + readonly property string move_medium: "\u00EE" + readonly property string newMaterial: "\u00EF" + readonly property string nextFile_large: "\u00F0" + readonly property string normalBar_small: "\u00F1" + readonly property string openLink: "\u00F2" + readonly property string openMaterialBrowser: "\u00F3" + readonly property string orientation: "\u00F4" + readonly property string orthCam_medium: "\u00F5" + readonly property string orthCam_small: "\u00F6" + readonly property string paddingEdge: "\u00F7" + readonly property string paddingFrame: "\u00F8" + readonly property string particleAnimation_medium: "\u00F9" + readonly property string pasteStyle: "\u00FA" + readonly property string paste_small: "\u00FB" + readonly property string pause: "\u00FC" + readonly property string pause_medium: "\u00FD" + readonly property string perspectiveCam_medium: "\u00FE" + readonly property string perspectiveCam_small: "\u00FF" + readonly property string pin: "\u0100" + readonly property string plane_medium: "\u0101" + readonly property string plane_small: "\u0102" + readonly property string play: "\u0103" + readonly property string playFill_medium: "\u0104" + readonly property string playOutline_medium: "\u0105" + readonly property string plus: "\u0106" + readonly property string pointLight_small: "\u0107" + readonly property string positioners_small: "\u0108" + readonly property string previewEnv_medium: "\u0109" + readonly property string previousFile_large: "\u010A" + readonly property string promote: "\u010B" + readonly property string properties_medium: "\u010C" + readonly property string readOnly: "\u010D" + readonly property string recent_medium: "\u010E" + readonly property string recordFill_medium: "\u010F" + readonly property string recordOutline_medium: "\u0110" + readonly property string redo: "\u0111" + readonly property string reload_medium: "\u0112" + readonly property string remove_medium: "\u0113" + readonly property string remove_small: "\u0114" + readonly property string rename_small: "\u0115" + readonly property string replace_small: "\u0116" + readonly property string resetView_small: "\u0117" + readonly property string restartParticles_medium: "\u0118" + readonly property string reverseOrder_medium: "\u0119" + readonly property string roatate_medium: "\u011A" + readonly property string rotationFill: "\u011B" + readonly property string rotationOutline: "\u011C" + readonly property string runProjFill_large: "\u011D" + readonly property string runProjOutline_large: "\u011E" + readonly property string s_anchors: "\u011F" + readonly property string s_annotations: "\u0120" + readonly property string s_arrange: "\u0121" + readonly property string s_boundingBox: "\u0122" + readonly property string s_component: "\u0123" + readonly property string s_connections: "\u0124" + readonly property string s_edit: "\u0125" + readonly property string s_enterComponent: "\u0126" + readonly property string s_eventList: "\u0127" + readonly property string s_group: "\u0128" + readonly property string s_layouts: "\u0129" + readonly property string s_merging: "\u012A" + readonly property string s_mouseArea: "\u012B" + readonly property string s_positioners: "\u012C" + readonly property string s_selection: "\u012D" + readonly property string s_snapping: "\u012E" + readonly property string s_timeline: "\u012F" + readonly property string s_visibility: "\u0130" + readonly property string saveAs_medium: "\u0131" + readonly property string saveLogs_medium: "\u0132" + readonly property string save_medium: "\u0133" + readonly property string scale_medium: "\u0134" + readonly property string search: "\u0135" + readonly property string search_small: "\u0136" + readonly property string sectionToggle: "\u0137" + readonly property string selectFill_medium: "\u0138" + readonly property string selectOutline_medium: "\u0139" + readonly property string selectParent_small: "\u013A" + readonly property string selection_small: "\u013B" + readonly property string settings_medium: "\u013C" + readonly property string signal_small: "\u013D" + readonly property string snapping_conf_medium: "\u013E" + readonly property string snapping_medium: "\u013F" + readonly property string snapping_small: "\u0140" + readonly property string sortascending_medium: "\u0141" + readonly property string sortdescending_medium: "\u0142" + readonly property string sphere_medium: "\u0143" + readonly property string sphere_small: "\u0144" + readonly property string splitColumns: "\u0145" + readonly property string splitRows: "\u0146" + readonly property string splitScreen_medium: "\u0147" + readonly property string spotLight_small: "\u0148" + readonly property string stackedContainer_small: "\u0149" + readonly property string startNode: "\u014A" + readonly property string step_medium: "\u014B" + readonly property string stop_medium: "\u014C" + readonly property string tableView_medium: "\u014D" + readonly property string testIcon: "\u014E" + readonly property string textAlignBottom: "\u014F" + readonly property string textAlignCenter: "\u0150" + readonly property string textAlignJustified: "\u0151" + readonly property string textAlignLeft: "\u0152" + readonly property string textAlignMiddle: "\u0153" + readonly property string textAlignRight: "\u0154" + readonly property string textAlignTop: "\u0155" + readonly property string textBulletList: "\u0156" + readonly property string textFullJustification: "\u0157" + readonly property string textNumberedList: "\u0158" + readonly property string textures_medium: "\u0159" + readonly property string tickIcon: "\u015A" + readonly property string tickMark_small: "\u015B" + readonly property string timeline_small: "\u015C" + readonly property string toEndFrame_medium: "\u015D" + readonly property string toNextFrame_medium: "\u015E" + readonly property string toPrevFrame_medium: "\u015F" + readonly property string toStartFrame_medium: "\u0160" + readonly property string topToolbar_annotations: "\u0161" + readonly property string topToolbar_closeFile: "\u0162" + readonly property string topToolbar_designMode: "\u0163" + readonly property string topToolbar_enterComponent: "\u0164" + readonly property string topToolbar_home: "\u0165" + readonly property string topToolbar_makeComponent: "\u0166" + readonly property string topToolbar_navFile: "\u0167" + readonly property string topToolbar_runProject: "\u0168" + readonly property string translationCreateFiles: "\u0169" + readonly property string translationCreateReport: "\u016A" + readonly property string translationExport: "\u016B" + readonly property string translationImport: "\u016C" + readonly property string translationSelectLanguages: "\u016D" + readonly property string translationTest: "\u016E" + readonly property string transparent: "\u016F" + readonly property string triState: "\u0170" + readonly property string triangleArcA: "\u0171" + readonly property string triangleArcB: "\u0172" + readonly property string triangleCornerA: "\u0173" + readonly property string triangleCornerB: "\u0174" + readonly property string unLinked: "\u0175" + readonly property string undo: "\u0176" + readonly property string unify_medium: "\u0177" + readonly property string unpin: "\u0178" + readonly property string upDownIcon: "\u0179" + readonly property string upDownSquare2: "\u017A" + readonly property string updateAvailable_medium: "\u017B" + readonly property string updateContent_medium: "\u017C" + readonly property string visibilityOff: "\u017D" + readonly property string visibilityOn: "\u017E" + readonly property string visible_medium: "\u017F" + readonly property string visible_small: "\u0180" + readonly property string warning_medium: "\u0181" + readonly property string wildcard: "\u0182" + readonly property string wizardsAutomotive: "\u0183" + readonly property string wizardsDesktop: "\u0184" + readonly property string wizardsGeneric: "\u0185" + readonly property string wizardsMcuEmpty: "\u0186" + readonly property string wizardsMcuGraph: "\u0187" + readonly property string wizardsMobile: "\u0188" + readonly property string wizardsUnknown: "\u0189" + readonly property string zoomAll: "\u018A" + readonly property string zoomIn: "\u018B" + readonly property string zoomIn_medium: "\u018C" + readonly property string zoomOut: "\u018D" + readonly property string zoomOut_medium: "\u018E" + readonly property string zoomSelection: "\u018F" readonly property font iconFont: Qt.font({ "family": controlIcons.name, diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/icons.ttf b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/icons.ttf index c650a651fe1738c65a10e8ea365893af4251f671..714cd4b7535ce94734ee47cee555def01ecd4ad7 100644 GIT binary patch delta 2356 zcmZo^W0}*=V#2`4z`(%E(7?dV5a8w(;=9N^@G%1;a{~hdgN%Q$zER|YG;0P1#s~%m zhJ@tY!~(5w9m$C%O7+YY3=9k$={c2Y9O`aa3=EtP7#ME+$w*C1(b}E0m4Sib4g&*& zSw==`BG=FVtqcr|A`A=+DjB&Y6$(`Xw-^{0cQ7z8y~xQ=PPA!b{l~z-_J@Ii=}~TC zMFHalhVKjvj1~+G3<`OPxv2-!tCuh^Ji5ccz;vM?zqmy7vIZlAHpA033=Fboz|Lo6 zU`SxSwkV$8<|_j?^9u$B28KrxlXt=BNsP9P1(WRM_gQ70bDKI ze%#Zz@9;$NH1T}lZQ#Aer@-gM*Tc`oZ^ZA!-zUH!utbnUFhy{O-~%BJp#)(D;Yq?L zg#U?{h!lt%5;YK=Cb~l`N~}oifY=vt74bCjbrLEPF%lajo=E(Xw5gYzAXOx_MVd`I zKzfgiiA?7=V)!UzQ&~jMg zsOMPbxW@6B;}<7BCj+NArw7g|&JE71obR|uxh!&Ja+PuQacyzk=X%BUjhma>GPf)4 zPVST3FL*F`XnFW}GWN32+Gr3MdQM67VOmJ|f5@=vHu7@VyX|kVT8jEBR82N6MX)AE`V#sdlLi zX-sJaY3tJNq|2n|W%y(q%lMI5ka;i5DQi_WOSW6~itI-@dO39r44dz8p697&_;11d zf?0tR9um{S;NLg!$7W0 zTmJ7;d2MZZCTV%?Hra5VdMOE>ckHZxpR%&EvocAuvcKaMm*V*|n>j&7Lqq1z0vQbr z=0--gKMUB{nB$q$)aCxpkyDqh;+4~8WYm^pybCgmyHfgJC!;Pm_l=n#Q~oKlvazww zy1~QEJYPe`1Z0khj7GT$E2{|`1M~m?U=J%YNHQ2P*fG>2`xoqPsBgv1&6qJf$tq^e zD5%UXY$OKpqluX@bFG%6qn5Ekos!Ex14CC=L&l3PO0^1~xH$e9aBy*PFrH`UW)x-S zWoH-g5|xz|{Zj)XnIg0l{+1|c$<`OyyBac1F?6*nl>Ikv4oJtp91uC@H#1W>Goyj5 zmaMFnECU1FjYX@MH)=PGj|;L{wgGr0I!IsPrW2Xg=4WgznYzXcp@d|aR^jR_u2(hTM(4(DS;jv#S!Ght;xHDz|p zGJsLc*qpgmJJ!KL+gPE3&q@WFBQB^|@s%rBGcvJq{Cfh*80_3!9E=y(xfx}dAWr^s zLBm9)T|vXukjd20Rb8PSVYCW9Q~(|9culp8vO(iIHLR37!30)lC^?!F4wS z0}F#BV>^`1${@ga7|Ld2kYwVRyzY^%dKSY*24)6E7ET5h1|!B`D4Uf*fpH;}&BkEF zcx5uvV`WB@$!d?~8BHd;JwCy2!l27w%%IC)#9+W+#9%U6{)s+^iLSA(k%5uPWS_?Y ulb1hfoUHj&fw5q-*VB7UOiX#3HD08!unQ(9=B5@UZf<;2F2;l0k^le@DPG_J delta 2212 zcmbQ!&eGDxV#2`4z`(%E(7?dV5a8w(;=9nL`X2)$(+dU$1{wcgeWS<)X;usjj1deB z3<=4(i3M8WIua92lWDO- zbd~fc85fxivKq2;WdF%U$xVbI%XmM$& zX=Q00(KgUNr2Rt2N+(OFL8tzd&JSG$-74K{dOUht^q%RL>0dDrF$ghOV6e~7!Eln{ z8zT>+CZj_}w~S{QKQJ*dX)>8%a?jMxbeZWlGe5IQW;e`j%V1SS~eLr`)svrvuw}V8QIy{1=&5b7qhRh@2R(c;gIF< z#?izv%W;Y0CdV_5@0?_u<~cobwsX#Kp5=VQCBfx@%Pm(fS0~pR*GaCc+*sUF+$Om* zxCglRx$kp-;lbtM;IYZ$nP-gWH!lyb4zCN|X5O>B&-jS=6!^^Wx#Mf%+va=9kHfFW z?}5LP|1|$o0Zah`0VV-O0lNal0_#5o%?S1go)i2jBq8KNs8(oB=&aBaVM1Zk!r8(_ z!c)R$L@-4tM3hAwisXrmh+G%>D=HvrP1KL*DbfF8lwy2hCd3?y`4X!U8x=bz_EOxT zc!T%}@sARC5}Xn`65b?gC1xZ(N#aYYNt%+hF6l(llVqvnh~x<=Y$>x+wxyg(`J<6) zmwF&gFD)l+R@$v}oeZvwmW*wgdYN;w7_u_54rQ}sr)4k5ev@Of`2pv7o_hZO7R+~; z6&Ms492s00LKzr@mDofDMZ}C1jm%6<%;XuxjE&4p1x?iW8QIiq8BNrbSxwB06^+DX z8AZhG8O_8*1x48O8P$}P*hEFxlogfKOc@nL6;&ZBzyh|6CT6MJf`Z&k3OoY7T1l*I zJc2U(;o2dBJb&wZjFg$=Rg7f#BD6#Nz;Xgc%6}gy8!=D%vz$2+ge4ReCH^dzP*h}` zAjtDinnzH8N6$!Dn@NC&F-<^_!%*DNP>@GZR>jCjMG(Z7Ff#l%OV3zXn^}-s*~mzR zIkITAqC}B|l9EJ`gdzh2GXu!e7nl_oxEbnY85|iHgdko5yPr{zolRLuO`lOwP|@6u z(b!0wQPD_Do>7j`*a+3X{EU2zU=ItLmigOe;cLH_$!v<93O6(yL9B$PIPm;5JKzliw-(_scd21^D8Ra3MiXpEc$jf}yW zk4;%gSzSq4i48g4m|N=E*{l*Iwf}iBZqt@buwv%=8_&2+)__-w*Fct;xt@ht)7n~- znWdhE2`c!{OVir=;3E|dMq8uMzbitGZ5V&ZNrpPoLC=aeP85md?BpJJ)Y*q#V#$!-68-pYh_hhrjy6QO$8yT1x7+E+OSQv~L!=P+d z1_j0?P&ONb5##mAU5}L+jVG^rEYD~>`P}0Zg2oKG42BFw47vs1*Dl+D8KKJw<)8=(AQdl;tyv-5gL9V$0YBV^F diff --git a/src/plugins/qmldesigner/components/componentcore/theme.h b/src/plugins/qmldesigner/components/componentcore/theme.h index 3ffc41c3c61..73184d391c2 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, + camera_medium, camera_small, centerHorizontal, centerVertical, From 36cdab7f989b185f5acf6594da12c813ef58284f Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Thu, 14 Mar 2024 15:54:33 +0200 Subject: [PATCH 176/176] QmlDesigner: Apply camera speed config icon Change-Id: I15ec173221908e6317f6ee8cb9d2355c8686e43b Reviewed-by: Miikka Heikkinen Reviewed-by: Qt CI Patch Build Bot --- share/qtcreator/qmldesigner/designericons.json | 3 +++ .../edit3dQmlSource/CameraSpeedConfigurationDialog.qml | 2 +- .../qmldesigner/components/componentcore/designericons.h | 1 + src/plugins/qmldesigner/components/edit3d/edit3dview.cpp | 2 +- 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/share/qtcreator/qmldesigner/designericons.json b/share/qtcreator/qmldesigner/designericons.json index 85c76066bb7..157012b312a 100644 --- a/share/qtcreator/qmldesigner/designericons.json +++ b/share/qtcreator/qmldesigner/designericons.json @@ -218,6 +218,9 @@ "iconName": "perspectiveCam_small" } }, + "CameraSpeedConfigIcon": { + "iconName": "camera_medium" + }, "EditColorIcon": { "iconName": "colorSelection_medium" }, diff --git a/share/qtcreator/qmldesigner/edit3dQmlSource/CameraSpeedConfigurationDialog.qml b/share/qtcreator/qmldesigner/edit3dQmlSource/CameraSpeedConfigurationDialog.qml index 219b485ca0d..2e2ff547002 100644 --- a/share/qtcreator/qmldesigner/edit3dQmlSource/CameraSpeedConfigurationDialog.qml +++ b/share/qtcreator/qmldesigner/edit3dQmlSource/CameraSpeedConfigurationDialog.qml @@ -57,7 +57,7 @@ Rectangle { HelperWidgets.IconIndicator { anchors.fill: parent - icon: StudioTheme.Constants.snapping_conf_medium // TODO update icon + icon: StudioTheme.Constants.camera_medium pixelSize: StudioTheme.Values.myIconFontSize * 1.4 iconColor: StudioTheme.Values.themeLinkIndicatorColorHover enabled: false diff --git a/src/plugins/qmldesigner/components/componentcore/designericons.h b/src/plugins/qmldesigner/components/componentcore/designericons.h index 3cb5a1e9731..1bce6581bdb 100644 --- a/src/plugins/qmldesigner/components/componentcore/designericons.h +++ b/src/plugins/qmldesigner/components/componentcore/designericons.h @@ -58,6 +58,7 @@ public: CameraIcon, CameraOrthographicIcon, CameraPerspectiveIcon, + CameraSpeedConfigIcon, ConnectionsIcon, CopyIcon, CreateIcon, diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp index 6073f565b61..911ee1fb156 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp @@ -1225,7 +1225,7 @@ void Edit3DView::createEdit3DActions() QKeySequence(), false, false, - toolbarIcon(DesignerIcons::SnappingConfIcon), // TODO proper icon + toolbarIcon(DesignerIcons::CameraSpeedConfigIcon), this, cameraSpeedConfigTrigger);
  3. }S91b>DZ%_=)~0qYYPfdi<>Ne7l(* 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 G5C8zBnB7?b literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/examples/doc/images/loginui1-properties-image.webp b/doc/qtdesignstudio/examples/doc/images/loginui1-properties-image.webp new file mode 100644 index 0000000000000000000000000000000000000000..ebab67ff78d88f1f99c9500b7ac3fdb2cbcee72a GIT binary patch literal 10768 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 literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/examples/doc/images/loginui1-properties-text.webp b/doc/qtdesignstudio/examples/doc/images/loginui1-properties-text.webp new file mode 100644 index 0000000000000000000000000000000000000000..a6b0e9c7e51bf00e685127fb9bd1ca7deacbead3 GIT binary patch literal 8996 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 literal 0 HcmV?d00001 diff --git a/doc/qtdesignstudio/examples/doc/images/loginui1-ready.jpg b/doc/qtdesignstudio/examples/doc/images/loginui1-ready.jpg deleted file mode 100644 index eef48f2a65305540d767326b0914688f4c9d709b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 89490 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́&csZ@4&&z>~$clX_l1of|lky4Y|V(M_JS_@@BQ= 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