diff --git a/dist/branding/qtdesignstudio/QtCreatorIDEBranding.cmake b/dist/branding/qtdesignstudio/QtCreatorIDEBranding.cmake index 204cedffdb1..69479620f8d 100644 --- a/dist/branding/qtdesignstudio/QtCreatorIDEBranding.cmake +++ b/dist/branding/qtdesignstudio/QtCreatorIDEBranding.cmake @@ -1,6 +1,6 @@ -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_VERSION "4.6.0") # The IDE version. +set(IDE_VERSION_COMPAT "4.6.0") # The IDE Compatibility version. +set(IDE_VERSION_DISPLAY "4.6.0") # The IDE display version. set(IDE_COPYRIGHT_YEAR "2024") # The IDE current copyright year. set(IDE_SETTINGSVARIANT "QtProject") # The IDE settings variation. diff --git a/doc/config/macros.qdocconf b/doc/config/macros.qdocconf index cf528917e73..751bc80ab71 100644 --- a/doc/config/macros.qdocconf +++ b/doc/config/macros.qdocconf @@ -19,6 +19,7 @@ macro.ouml.HTML = "ö" macro.B2Q = "Boot to Qt" macro.Q3DS = "Qt 3D Studio" macro.QA = "Qt Assistant" +macro.QAC = "Qt Academy" macro.QB = "Qt Bridge" macro.QBPS = "Qt Bridge for Adobe Photoshop" macro.QBXD = "Qt Bridge for Adobe XD" diff --git a/doc/qtcreator/src/qtquick/qtquick-modules-with-plugins.qdoc b/doc/qtcreator/src/qtquick/qtquick-modules-with-plugins.qdoc index 46651d8748a..e1f7e3bce54 100644 --- a/doc/qtcreator/src/qtquick/qtquick-modules-with-plugins.qdoc +++ b/doc/qtcreator/src/qtquick/qtquick-modules-with-plugins.qdoc @@ -82,7 +82,7 @@ \uicontrol {Reset Code Model} to reset the code model. \if defined(qtdesignstudio) For more information about how to show the \uicontrol {Tools} menu, see - \l{Customizing the Menu}. + \l{Customizing the Menu Bar}. \endif \if defined(qtcreator) diff --git a/doc/qtdesignstudio/config/style/qt5-sidebar.html b/doc/qtdesignstudio/config/style/qt5-sidebar.html index 0a7c93702a8..994ecd3db30 100644 --- a/doc/qtdesignstudio/config/style/qt5-sidebar.html +++ b/doc/qtdesignstudio/config/style/qt5-sidebar.html @@ -1,18 +1,109 @@ +
+ +
- -

Qt Design Studio Manual

+

Getting Started

+
+
+
+

Wireframing

+
+
+ +
+
+
+
+

Prototyping

+
+ +
+
+
+

Motion Design

+
+ +
+
+
+

Implementing Applications

+
+ +
+
+
+

Advanced Designer Topics

+
+ +
+
+
+

Developer Topics

+
+ +
+
+
+

Help

+
+ +
diff --git a/doc/qtdesignstudio/images/bleed-scale-1.webp b/doc/qtdesignstudio/images/bleed-scale-1.webp new file mode 100644 index 00000000000..cfe9d7bd7e0 Binary files /dev/null and b/doc/qtdesignstudio/images/bleed-scale-1.webp differ diff --git a/doc/qtdesignstudio/images/bleed-scale-8.webp b/doc/qtdesignstudio/images/bleed-scale-8.webp new file mode 100644 index 00000000000..3d4ef0b7834 Binary files /dev/null and b/doc/qtdesignstudio/images/bleed-scale-8.webp differ diff --git a/doc/qtdesignstudio/images/bleed-scale-no.webp b/doc/qtdesignstudio/images/bleed-scale-no.webp new file mode 100644 index 00000000000..e102ae7501a Binary files /dev/null and b/doc/qtdesignstudio/images/bleed-scale-no.webp differ diff --git a/doc/qtdesignstudio/images/ext-scene-env-navigator.webp b/doc/qtdesignstudio/images/ext-scene-env-navigator.webp new file mode 100644 index 00000000000..675a9aedfcc Binary files /dev/null and b/doc/qtdesignstudio/images/ext-scene-env-navigator.webp differ diff --git a/doc/qtdesignstudio/images/glow-additive-blend.webp b/doc/qtdesignstudio/images/glow-additive-blend.webp new file mode 100644 index 00000000000..418628fc8be Binary files /dev/null and b/doc/qtdesignstudio/images/glow-additive-blend.webp differ diff --git a/doc/qtdesignstudio/images/glow-bicubic.webp b/doc/qtdesignstudio/images/glow-bicubic.webp new file mode 100644 index 00000000000..216a2487088 Binary files /dev/null and b/doc/qtdesignstudio/images/glow-bicubic.webp differ diff --git a/doc/qtdesignstudio/images/glow-example-project.webp b/doc/qtdesignstudio/images/glow-example-project.webp new file mode 100644 index 00000000000..2163fee2d91 Binary files /dev/null and b/doc/qtdesignstudio/images/glow-example-project.webp differ diff --git a/doc/qtdesignstudio/images/glow-example.webp b/doc/qtdesignstudio/images/glow-example.webp new file mode 100644 index 00000000000..4de776aa443 Binary files /dev/null and b/doc/qtdesignstudio/images/glow-example.webp differ diff --git a/doc/qtdesignstudio/images/glow-high-quality.webp b/doc/qtdesignstudio/images/glow-high-quality.webp new file mode 100644 index 00000000000..80434dde67a Binary files /dev/null and b/doc/qtdesignstudio/images/glow-high-quality.webp differ diff --git a/doc/qtdesignstudio/images/glow-no-enhancment.webp b/doc/qtdesignstudio/images/glow-no-enhancment.webp new file mode 100644 index 00000000000..87d90da35f1 Binary files /dev/null and b/doc/qtdesignstudio/images/glow-no-enhancment.webp differ diff --git a/doc/qtdesignstudio/images/glow-properties.webp b/doc/qtdesignstudio/images/glow-properties.webp new file mode 100644 index 00000000000..b2d22736eb3 Binary files /dev/null and b/doc/qtdesignstudio/images/glow-properties.webp differ diff --git a/doc/qtdesignstudio/images/glow-replace-blend.webp b/doc/qtdesignstudio/images/glow-replace-blend.webp new file mode 100644 index 00000000000..06c8e8c6692 Binary files /dev/null and b/doc/qtdesignstudio/images/glow-replace-blend.webp differ diff --git a/doc/qtdesignstudio/images/glow-screen-blend.webp b/doc/qtdesignstudio/images/glow-screen-blend.webp new file mode 100644 index 00000000000..6a18367cb92 Binary files /dev/null and b/doc/qtdesignstudio/images/glow-screen-blend.webp differ diff --git a/doc/qtdesignstudio/images/glow-softlight-blend.webp b/doc/qtdesignstudio/images/glow-softlight-blend.webp new file mode 100644 index 00000000000..3ac9ef26406 Binary files /dev/null and b/doc/qtdesignstudio/images/glow-softlight-blend.webp differ diff --git a/doc/qtdesignstudio/images/glow_all_blur_levels.webp b/doc/qtdesignstudio/images/glow_all_blur_levels.webp new file mode 100644 index 00000000000..a5cc30d9cc0 Binary files /dev/null and b/doc/qtdesignstudio/images/glow_all_blur_levels.webp differ diff --git a/doc/qtdesignstudio/images/glow_high_blur_levels.webp b/doc/qtdesignstudio/images/glow_high_blur_levels.webp new file mode 100644 index 00000000000..7cdf15a5c5c Binary files /dev/null and b/doc/qtdesignstudio/images/glow_high_blur_levels.webp differ diff --git a/doc/qtdesignstudio/images/glow_low_blur_levels.webp b/doc/qtdesignstudio/images/glow_low_blur_levels.webp new file mode 100644 index 00000000000..a59ed6cd7e8 Binary files /dev/null and b/doc/qtdesignstudio/images/glow_low_blur_levels.webp differ diff --git a/doc/qtdesignstudio/images/qtcreator-qt-design-studio-project.webp b/doc/qtdesignstudio/images/qtcreator-qt-design-studio-project.webp new file mode 100644 index 00000000000..3e98d42ce2a Binary files /dev/null and b/doc/qtdesignstudio/images/qtcreator-qt-design-studio-project.webp differ diff --git a/doc/qtdesignstudio/images/studio-effects-background-blur.webp b/doc/qtdesignstudio/images/studio-effects-background-blur.webp new file mode 100644 index 00000000000..b1fd2172637 Binary files /dev/null and b/doc/qtdesignstudio/images/studio-effects-background-blur.webp differ diff --git a/doc/qtdesignstudio/images/studio-effects-drop-shadow.webp b/doc/qtdesignstudio/images/studio-effects-drop-shadow.webp new file mode 100644 index 00000000000..7de04ead324 Binary files /dev/null and b/doc/qtdesignstudio/images/studio-effects-drop-shadow.webp differ diff --git a/doc/qtdesignstudio/images/studio-effects-inner-shadow.webp b/doc/qtdesignstudio/images/studio-effects-inner-shadow.webp new file mode 100644 index 00000000000..35029035515 Binary files /dev/null and b/doc/qtdesignstudio/images/studio-effects-inner-shadow.webp differ diff --git a/doc/qtdesignstudio/images/studio-effects-layer-blur.webp b/doc/qtdesignstudio/images/studio-effects-layer-blur.webp new file mode 100644 index 00000000000..32c2c66779f Binary files /dev/null and b/doc/qtdesignstudio/images/studio-effects-layer-blur.webp differ diff --git a/doc/qtdesignstudio/images/studio-effects-show-shadow-behind.webp b/doc/qtdesignstudio/images/studio-effects-show-shadow-behind.webp new file mode 100644 index 00000000000..5b7411bd2c0 Binary files /dev/null and b/doc/qtdesignstudio/images/studio-effects-show-shadow-behind.webp differ diff --git a/doc/qtdesignstudio/images/studio-effects.webp b/doc/qtdesignstudio/images/studio-effects.webp new file mode 100644 index 00000000000..1910dc40afb Binary files /dev/null and b/doc/qtdesignstudio/images/studio-effects.webp differ diff --git a/doc/qtdesignstudio/images/studio-flow-decision-preview.png b/doc/qtdesignstudio/images/studio-flow-decision-preview.png deleted file mode 100644 index c0c367d1439..00000000000 Binary files a/doc/qtdesignstudio/images/studio-flow-decision-preview.png and /dev/null differ diff --git a/doc/qtdesignstudio/images/studio-flow-decision-preview.webp b/doc/qtdesignstudio/images/studio-flow-decision-preview.webp new file mode 100644 index 00000000000..e3ac10b0b24 Binary files /dev/null and b/doc/qtdesignstudio/images/studio-flow-decision-preview.webp differ diff --git a/doc/qtdesignstudio/images/studio-flow-decision-properties.png b/doc/qtdesignstudio/images/studio-flow-decision-properties.png deleted file mode 100644 index dbaba5338f7..00000000000 Binary files a/doc/qtdesignstudio/images/studio-flow-decision-properties.png and /dev/null differ diff --git a/doc/qtdesignstudio/images/studio-flow-decision-properties.webp b/doc/qtdesignstudio/images/studio-flow-decision-properties.webp new file mode 100644 index 00000000000..4029d5cd195 Binary files /dev/null and b/doc/qtdesignstudio/images/studio-flow-decision-properties.webp differ diff --git a/doc/qtdesignstudio/images/studio-flow-decision.png b/doc/qtdesignstudio/images/studio-flow-decision.png deleted file mode 100644 index 0880a14559b..00000000000 Binary files a/doc/qtdesignstudio/images/studio-flow-decision.png and /dev/null differ diff --git a/doc/qtdesignstudio/images/studio-flow-decision.webp b/doc/qtdesignstudio/images/studio-flow-decision.webp new file mode 100644 index 00000000000..df7b18ab93f Binary files /dev/null and b/doc/qtdesignstudio/images/studio-flow-decision.webp differ diff --git a/doc/qtdesignstudio/images/studio-flow-events-assign.png b/doc/qtdesignstudio/images/studio-flow-events-assign.png deleted file mode 100644 index c585dd80335..00000000000 Binary files a/doc/qtdesignstudio/images/studio-flow-events-assign.png and /dev/null differ diff --git a/doc/qtdesignstudio/images/studio-flow-events-assign.webp b/doc/qtdesignstudio/images/studio-flow-events-assign.webp new file mode 100644 index 00000000000..53468aca42b Binary files /dev/null and b/doc/qtdesignstudio/images/studio-flow-events-assign.webp differ diff --git a/doc/qtdesignstudio/images/studio-flow-events-event-list.webp b/doc/qtdesignstudio/images/studio-flow-events-event-list.webp new file mode 100644 index 00000000000..b33b940812c Binary files /dev/null and b/doc/qtdesignstudio/images/studio-flow-events-event-list.webp differ diff --git a/doc/qtdesignstudio/images/studio-flow-item-component.webp b/doc/qtdesignstudio/images/studio-flow-item-component.webp new file mode 100644 index 00000000000..8c1ea982b13 Binary files /dev/null and b/doc/qtdesignstudio/images/studio-flow-item-component.webp differ diff --git a/doc/qtdesignstudio/images/studio-flow-states-item-properties.webp b/doc/qtdesignstudio/images/studio-flow-states-item-properties.webp new file mode 100644 index 00000000000..14e024dab53 Binary files /dev/null and b/doc/qtdesignstudio/images/studio-flow-states-item-properties.webp differ diff --git a/doc/qtdesignstudio/images/studio-flow-transition-properties-question.png b/doc/qtdesignstudio/images/studio-flow-transition-properties-question.png deleted file mode 100644 index 80cb7ad1a99..00000000000 Binary files a/doc/qtdesignstudio/images/studio-flow-transition-properties-question.png and /dev/null differ diff --git a/doc/qtdesignstudio/images/studio-flow-transition-properties-question.webp b/doc/qtdesignstudio/images/studio-flow-transition-properties-question.webp new file mode 100644 index 00000000000..26250a17c12 Binary files /dev/null and b/doc/qtdesignstudio/images/studio-flow-transition-properties-question.webp differ diff --git a/doc/qtdesignstudio/images/studio-flow-view.png b/doc/qtdesignstudio/images/studio-flow-view.png deleted file mode 100644 index 1eda708fcb4..00000000000 Binary files a/doc/qtdesignstudio/images/studio-flow-view.png and /dev/null differ diff --git a/doc/qtdesignstudio/images/studio-flow-view.webp b/doc/qtdesignstudio/images/studio-flow-view.webp new file mode 100644 index 00000000000..21db99f0430 Binary files /dev/null and b/doc/qtdesignstudio/images/studio-flow-view.webp differ diff --git a/doc/qtdesignstudio/images/studio-flow-wildcard-properties.png b/doc/qtdesignstudio/images/studio-flow-wildcard-properties.png deleted file mode 100644 index 944fd79431d..00000000000 Binary files a/doc/qtdesignstudio/images/studio-flow-wildcard-properties.png and /dev/null differ diff --git a/doc/qtdesignstudio/images/studio-flow-wildcard-properties.webp b/doc/qtdesignstudio/images/studio-flow-wildcard-properties.webp new file mode 100644 index 00000000000..66ade499429 Binary files /dev/null and b/doc/qtdesignstudio/images/studio-flow-wildcard-properties.webp differ diff --git a/doc/qtdesignstudio/images/studio-flow-wildcard.webp b/doc/qtdesignstudio/images/studio-flow-wildcard.webp new file mode 100644 index 00000000000..8c0805cedc5 Binary files /dev/null and b/doc/qtdesignstudio/images/studio-flow-wildcard.webp differ diff --git a/doc/qtdesignstudio/images/studio-project-cmake-generation.webp b/doc/qtdesignstudio/images/studio-project-cmake-generation.webp new file mode 100644 index 00000000000..18058f785ac Binary files /dev/null and b/doc/qtdesignstudio/images/studio-project-cmake-generation.webp differ diff --git a/doc/qtdesignstudio/images/studio-project-export-advanced-options.webp b/doc/qtdesignstudio/images/studio-project-export-advanced-options.webp deleted file mode 100644 index 33be8549a70..00000000000 Binary files a/doc/qtdesignstudio/images/studio-project-export-advanced-options.webp and /dev/null differ diff --git a/doc/qtdesignstudio/images/studio-project-export-advanced.webp b/doc/qtdesignstudio/images/studio-project-export-advanced.webp deleted file mode 100644 index d6f1189093e..00000000000 Binary files a/doc/qtdesignstudio/images/studio-project-export-advanced.webp and /dev/null differ diff --git a/doc/qtdesignstudio/images/studio-project-export.webp b/doc/qtdesignstudio/images/studio-project-export.webp index 8847dd945f8..b79ce329ee1 100644 Binary files a/doc/qtdesignstudio/images/studio-project-export.webp and b/doc/qtdesignstudio/images/studio-project-export.webp differ diff --git a/doc/qtdesignstudio/src/best practices/best-practices-glow.qdoc b/doc/qtdesignstudio/src/best practices/best-practices-glow.qdoc new file mode 100644 index 00000000000..22a71ec681d --- /dev/null +++ b/doc/qtdesignstudio/src/best practices/best-practices-glow.qdoc @@ -0,0 +1,200 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +/*! + \previouspage best-practices.html + \page best-practices-glow.html + \nextpage {Examples} + + \title Creating Glow and Bloom Effects + + In \QDS, you can add a glow and bloom effect to your 3D scene using the + \uicontrol ExtendedSceneEnvironment component (available in Qt 6.5 and later). With this effect, + you can, for example, create glow around illuminated areas (such as material or skyboxes when + using image-based lighting) or add ambient light. Using the glow effect is one way to make your + scene more realistic. + + \image glow-example.webp + + \section1 Creating a Project with ExtendedSceneEnvironment + + To create a project with \uicontrol ExtendedSceneEnvironment as the default + scene environment, use the \uicontrol {3D Extended} project preset. + + For more information about creating projects, see \l{Creating Projects}. + + \section1 Adding ExtendedSceneEnvironment to an Existing Project + + To add \uicontrol {ExtendedSceneEnvironment} to an existing project, drag the + \uicontrol {ExtendedSceneEnvironment} component from \uicontrol Components to + the \uicontrol 2D or \uicontrol Navigator view. + + \image ext-scene-env-navigator.webp + + \section1 Enabling the Glow Effect + + To enable the glow effect, select \e SceneEnvironment in the \uicontrol Navigator view and + then, in the \uicontrol Properties view, select \uicontrol Enabled in the + \uicontrol Glow section. + + \image glow-properties.webp + + \note When setting up or experimenting with the glow effect, use the \l {Blend Modes}{Replace} + blend mode to see the effect more clearly. + + \section1 The Flashlight Example Project + + The flashlight example used in this documentation is available from the \uicontrol Examples + section of the \QDS \uicontrol Welcome page. + + You can use the project to experiment with the \uicontrol Glow properties. When you run the + project, you can control most properties with UI controls. Another way to experiment with + properties is to change them directly in the \uicontrol Properties view in \QDS and see the + changes live in the \uicontrol 2D view. + + \image glow-example-project.webp + + \section1 Basic Properties + + Usually, the best way to achieve a realistic glow effect in your 3D scene is to adjust + the \uicontrol {Strength}, \uicontrol {Intensity}, and \uicontrol {Bloom} + properties together. + + \section2 Strength + + Sets the strength of the glow. If this property has a 0 value, the glow effect is disabled. The + strength is a scale factor (multiplier) applied per level. This means that + with more levels enabled in \l {Blur Levels}, a larger \uicontrol {strength} value has + a more pronounced effect. + + \section2 Intensity + + Sets the intensity of the glow. The intensity is effectively a scale factor (multiplier) for the + accumulated glow color (including all levels). + + \section2 Bloom + + Sets the overall illumination of the whole scene. + + \section2 Lower Threshold + + Sets the minimum level of brightness for the glow. + + \section2 Upper Threshold + + Sets the maximum level of brightness for the glow. + + \section2 HDR Scale + + Sets how much the glow bleeds (or extends) beyond the borders of bright areas + in the scene. The range for this property is 0-8. As seen in the example images below, a high + value gives a very modest bleed, while a low number results in a much more visible bleed. + + \table + \header + \li HDR Scale + \li Example + \row + \li Bloom disabled + \li \image bleed-scale-no.webp + \row + \li 8 + \li \image bleed-scale-8.webp + \row + \li 1 + \li \image bleed-scale-1.webp + \endtable + + \section1 Blur Levels + + Sets which of the blur passes to apply to the glow effect. + + The higher the level is, the more the glow effect spreads around the light source, + and the softer the glow is. + + As seen in the example image below, lower blur levels produce an intense glow within a limited + area, while higher blur levels produce a softer glow in a more extensive area. Combine blur + levels to get the desired result. + + \table + \header + \li Blur level + \li Example + \row + \li 1, 2, 3 + \li \image glow_low_blur_levels.webp + \row + \li 5, 6, 7 + \li \image glow_high_blur_levels.webp + \row + \li 1, 2, 3, 4, 5, 6, 7 + \li \image glow_all_blur_levels.webp + \endtable + + \section2 Blend Modes + + The following blend modes are available: + + \table + \header + \li Blend mode + \li Description + \li Example + \row + \li Additive + \li Often recommended for outdoor scenes with a visible sky or sun. + \li \image glow-additive-blend.webp + \row + \li Screen + \li Similar to \uicontrol {Additive}, but the result is less bright. + \li \image glow-screen-blend.webp + \row + \li SoftLight + \li Often recommended for in-door environments. + \li \image glow-softlight-blend.webp + \row + \li Replace + \li Does not perform any blending, but displays only the contribution + the glow effect would blend with the actual content. In practice, this is useful for + experimenting and troubleshooting when setting up the glow effect. + \li \image glow-replace-blend.webp + \endtable + + \section1 Improvement Properties + + The \uicontrol{High Quality} and \uicontrol{Bicubical Upsampling} + properties improve the quality of the glow blur by upsampling. Using these properties + may slow down the performance of the application. You can also try using the + \uicontrol Dithering property in the \uicontrol sceneEnvironment instead. In some cases, + \uicontrol Dithering renders a similar result but at a lower cost. + + \section2 High Quality + + Increases the samples used for the glow when downsampling to improve the quality of the glow + effect. + + \section2 Bicubical Upsampling + + Reduces the aliasing artifacts and boxing in the glow effect. + + \section2 Examples + + These examples show a zoomed-in image of each effect: + + \table + \header + \li Effect + \li Example + \row + \li No effect + \li \image glow-no-enhancment.webp + \row + \li High Quality + \li \image glow-high-quality.webp + \row + \li Bicubic Upsampling + \li \image glow-bicubic.webp + \endtable + + +*/ diff --git a/doc/qtdesignstudio/src/best-practices.qdoc b/doc/qtdesignstudio/src/best-practices.qdoc new file mode 100644 index 00000000000..4abcc80f6af --- /dev/null +++ b/doc/qtdesignstudio/src/best-practices.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-terms.html + \page best-practices.html + \nextpage best-practices-glow.html + + \title Best Practices + + \section1 Graphics + + \list + \li \l {Creating Glow and Bloom Effects} + \endlist + + \section1 Performance + + \list + \li \l {Optimizing Designs} + \li \l {QML Performance Considerations And Suggestions} + \li \l {Creating Optimized 3D Scenes} + \li \l {Using Components Economically} + \endlist + + \section1 Projects + + \list + \li \l {Converting Qt 5 Projects into Qt 6 Projects} + \li \l {Converting UI Projects to Applications} + \endlist + + \section1 Workflow + + \list + \li \l {Designer-Developer Workflow} + \endlist +*/ diff --git a/doc/qtdesignstudio/src/components/qtquick-images.qdoc b/doc/qtdesignstudio/src/components/qtquick-images.qdoc index 176e4409f8a..cbece71a4b5 100644 --- a/doc/qtdesignstudio/src/components/qtquick-images.qdoc +++ b/doc/qtdesignstudio/src/components/qtquick-images.qdoc @@ -44,6 +44,9 @@ If you need to display animated images, such as GIFs, use the \l {Animated Image} component. + \note Currently, the supported image formats include JPEG, JPG, PNG, SVG, + HDR, KTX, BMP, TTF, TIFF, WEBP, and GIF. + \section1 Image Size \image qtquick-designer-image-properties.png "Image properties" diff --git a/doc/qtdesignstudio/src/components/qtquick-preset-components.qdoc b/doc/qtdesignstudio/src/components/qtquick-preset-components.qdoc index 4920cf582dd..267517cb6c7 100644 --- a/doc/qtdesignstudio/src/components/qtquick-preset-components.qdoc +++ b/doc/qtdesignstudio/src/components/qtquick-preset-components.qdoc @@ -31,6 +31,7 @@ \li \l {UI Controls} \li \l {Lists and Other Data Models} \li \l {2D Effects} + \li \l {Design Effects} \li \l {Logic Helpers} \li \l Animations \endlist diff --git a/doc/qtdesignstudio/src/developers/studio-designer-developer-workflow.qdoc b/doc/qtdesignstudio/src/developers/studio-designer-developer-workflow.qdoc index e8c6c8e3296..e214b7709e3 100644 --- a/doc/qtdesignstudio/src/developers/studio-designer-developer-workflow.qdoc +++ b/doc/qtdesignstudio/src/developers/studio-designer-developer-workflow.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 /*! @@ -19,10 +19,6 @@ other files that are needed to implement the application logic and to prepare the application for production. - Use the \l{Using Git}{Git} version control system to ensure that changes - are not lost when files are passed back and forth between designers and - developers. - \QDS \l{Creating Projects}{projects} come with boilerplate code for a working Qt 6 application that you can build and run in Qt Creator using CMake. Therefore, you can open, build, and run the projects with Qt Creator. @@ -31,58 +27,53 @@ \e CMakeLists.txt file as the project file. This enables you to share your project as a fully working C++ application with developers. - If you use Git, you can clone an example project - \l{https://git.qt.io/public-demos/qtdesign-studio/-/tree/master/playground/AuroraCluster0} - {here}. + To export a \QDS project for Qt Creator, you need: + + \list + \li Qt Creator 13.0 or above. + \li \QDS 4.5 or above. + \endlist \section1 Exporting a \QDS Project - \QDS uses a different project format than Qt Creator. \QDS does not build the project, - it uses a pre-compiled \l{QML runtime} to run the project. To export a \QDS project for the - Qt Creator, follow the process: + \list 1 + \li \l {Creating a Project} {Create} or open your \QDS project with \QDS 4.5 or above. - \list 1 - \li Open the project you want to export in \QDS. - \li Select \uicontrol {File} > \uicontrol {Export Project} > \uicontrol {Generate CMake Build Files}. - \image studio-project-export.webp "Export the \QDS project for Qt Creator" + \note If you are creating a new project in \QDS, select the + \uicontrol {Target Qt Version} that is not higher than the Qt version + used in your Qt Creator. - \li Select \uicontrol {Details} to access the \uicontrol {Advanced Options}. - \image studio-project-export-advanced.webp "Access Advanced Options in the project exporter" + \li Go to \uicontrol {File} > \uicontrol {Export Project} + > \uicontrol {Enable Automatic CMake Generation}. This creates a + \e {CMakeLists.txt} file in your project folder. - \note The project exporter has default settings selected. This works better if the project - is combined with an existing Qt project. + \note Enabling this option tracks the changes made to the project in Qt Creator + and automatically updates in \QDS. The connection works unless you + deactivate the option. - \li Select all the options here. This allows to export the - complete project. So, it can be compiled as a stand-alone application. - \image studio-project-export-advanced-options.webp "Select all the options in the project exporter" + \image studio-project-export.webp "Exporting Qt Design Studio project" + \endlist - \note If you copy this export on top of the existing Qt Creator project - it overwrites the existing project. Hence, the default selected options in - the exporter only exports the QML-specific items. You get a list of - warnings at the bottom part of the exporter that denotes exactly which parts - of the project gets overwritten. - \endlist + \section1 Opening the \QDS Project in Qt Creator - \section1 Using the Exported Project in Qt Creator + Open the \e {CMakeLists.txt} file in Qt Creator. To open: - After exporting the project from the \QDS, you have to open it from Qt Creator. + \list 1 + \li In Qt Creator, select \uicontrol File > \uicontrol {Open File or Project}. + \li Browse through your project directory and select the \e {CMakeLists.txt}. + Then select \uicontrol Open. - If you have used any version before \QDS 4.0 to create the project, manually include this code - in the \e {CMakeLists.txt} file so the exported project works in Qt Creator. + \image studio-project-cmake-generation.webp "Project folder after CMake generation" - \code - set(BUILD_QDS_COMPONENTS ON CACHE BOOL "Build design studio components") + \li Select the Qt version and then \uicontrol {Configure Project}. - set(CMAKE_INCLUDE_CURRENT_DIR ON) - - if (${BUILD_QDS_COMPONENTS}) - include(${CMAKE_CURRENT_SOURCE_DIR}/qmlcomponents) - endif () - - include(${CMAKE_CURRENT_SOURCE_DIR}/qmlmodules) - \endcode - - \note If you have created the project with the \QDS version 4.0 or above, you already have this code in - \e {CMakeLists.txt} by default. + \note If your \QDS project was created with a more updated Qt than the one + available in Qt Creator, the import doesn't work. Use + \l {Get and Install Qt with Qt Online Installer} {Qt Online Installer} + to install the latest Qt version. If successfully opened, all the files are + accessible in the \uicontrol Projects view. + \image qtcreator-qt-design-studio-project.webp "Qt Design studio projects in Qt Creator after successful import" + \li To run the project, select \uicontrol Run. + \endlist */ diff --git a/doc/qtdesignstudio/src/how-to/qtdesignstudio-live-preview-desktop.qdoc b/doc/qtdesignstudio/src/how-to/qtdesignstudio-live-preview-desktop.qdoc index 5145bc23972..0178d7f6666 100644 --- a/doc/qtdesignstudio/src/how-to/qtdesignstudio-live-preview-desktop.qdoc +++ b/doc/qtdesignstudio/src/how-to/qtdesignstudio-live-preview-desktop.qdoc @@ -24,9 +24,12 @@ \uicontrol {Preview File}. \endlist - To preview the whole UI, select \uicontrol {Live Preview} + To preview the whole application, select \uicontrol {Live Preview} when viewing the main QML file of the project. + To preview the application in a different zoom level, right-click the \uicontrol {Live Preview} + button and select the zoom level. + \section1 Overriding the Preview Tool By default, the QML runtime is used for previewing. diff --git a/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-overview.qdoc b/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-overview.qdoc index b721acfa9e3..798ca9551c6 100644 --- a/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-overview.qdoc +++ b/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-overview.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2020 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only // Note: The \page value is hard-coded as a link in Qt Bridge for Figma. @@ -29,5 +29,9 @@ To get the best results when you use \QBF to export designs from Figma, you should follow the guidelines for working with Figma and organizing your assets. + \endlist + + \include qtdesignstudio-qt-academy.qdocinc qt-academy-using-qt-bridge-for-figma + */ diff --git a/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-setup.qdoc b/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-setup.qdoc index 33d130f3360..00e9586df25 100644 --- a/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-setup.qdoc +++ b/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-setup.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2020 The Qt Company Ltd. +// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! @@ -25,4 +25,6 @@ \endlist You can launch the installed Figma plugin from \uicontrol Plugins > \uicontrol {\QBF} in Figma. + + \include qtdesignstudio-qt-academy.qdocinc qt-academy-using-qt-bridge-for-figma */ diff --git a/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-using.qdoc b/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-using.qdoc index b189500f491..22cc72105e7 100644 --- a/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-using.qdoc +++ b/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-using.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 /*! @@ -247,4 +247,6 @@ \uicontrol Home tab) to default values. This means that you will lose all your changes to the settings. \endtable + + \include qtdesignstudio-qt-academy.qdocinc qt-academy-using-qt-bridge-for-figma */ diff --git a/doc/qtdesignstudio/src/qtdesignstudio-app-flows.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-app-flows.qdoc index 5e4a503800d..1be7f143117 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-app-flows.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-app-flows.qdoc @@ -8,25 +8,25 @@ \title Designing Application Flows - \image studio-flow-view.png "Application flow in the 2D view" + \image studio-flow-view.webp "Application flow in the 2D view" In \QDS, a \e {flow view} represents a schematic diagram. It consists of \e {flow items} that represent the screens in the UI and \e {transition lines} that connect them, thus illustrating the possible user pathways - through the UI. You use \e {action areas} as starting points for transition - lines. You can attach effects to transition lines, such as fade or push, + through the UI. \e {Action areas} are clickable starting points for transition + lines. Attach effects to transition lines, such as fade or push, to determine what users see when one flow item changes into another. - You can use \e {flow decisions} to set up alternative pathways between - flow items in the UI. For example, if user input determines which flow item - should open next, you can test the different scenarios in the prototype - by having a dialog pop up where you can select which flow item to show next. + Use \e {flow decisions} to set up alternative pathways between flow items in the UI. For + example, if user input determines which flow item should open next, test the different + scenarios in the prototype with the decision dialog where you can select which flow item to + show next. Especially on mobile and embedded platforms, the application might need to react to external events from the platform, such as notifications or other - applications requiring the users' attention. You can use \e {flow wildcards} - to determine the priority of flow items by adding them to positive and - negative lists. + applications requiring the users' attention. Use \e {flow wildcards} + to determine the priority of flow items by adding them to allow and + block lists. To design application flows: @@ -40,14 +40,17 @@ \l{Adding Flow Items}. \li Use context menu commands to add action areas and transitions, as described in \l{Adding Action Areas and Transitions}. + \endlist + + Additionally, to create a more advanced application flow: + + \list \li Use context menu commands to apply effects to transitions, as described in \l{Applying Effects to Transitions}. - \li When you are ready for production, use the event list simulator - to replace transition lines with connections to real signals - from UI controls, as described in \l{Simulating Events}. - \li To set up alternative pathways between flow items, use - \uicontrol {Flow Decision} components from - \uicontrol Components > \uicontrol {Flow View}, as described in + \li Use the event list simulator to replace transition lines with connections to real + signals from UI controls, as described in \l{Simulating Events}. + \li Use \uicontrol {Flow Decision} components from \uicontrol Components > \uicontrol {Flow + View} to set up alternative pathways between flow items, as described in \l{Simulating Conditions}. \li Use \l{Working with States}{states} in flows to modify the appearance of components on screens in response to user interaction, as @@ -256,8 +259,9 @@ \li Drag the action area to the UI control that you want to connect to the other flow item. For example, to a button that opens another flow item when clicked. - \li Double-click the action area and drag the transition line to the - flow item you want to connect to. + \li Double-click the action area and drag the transition line to the flow item you want to + connect to. Alternatively, select the action area, right-click it to open the + context-menu, and then select \uicontrol Connect and the flow item from the list. \li In \l Properties, modify the properties of the action area and transition line. \endlist @@ -376,13 +380,13 @@ \title Applying Effects to Transitions - You can apply effects, such as fade, move, or push to + You can apply effects, such as fade, move, or push, to \l{Adding Action Areas and Transitions}{transitions} between \l{Adding Flow Items}{flow items}. A fade effect makes the first flow item appear to fade out, while the next flow item fades in. A move effect makes the second flow item appear to move in over the - first flow item, while the push effect appears to make a flow item - push out the previous one. You can also design and use custom effects. + first flow item. The push effect makes a flow item appear to push out the previous one. + You can also use your own custom effects that you have created with some other tool. The transition direction determines the direction the new flow item appears from: left, right, top, bottom. You can set the duration of the effect and @@ -392,34 +396,42 @@ \list 1 \li Select a transition line in the \l {2D} view. - \li In the context menu, select \uicontrol {Flow} > - \uicontrol {Assign Flow Effects}, and then select the effect - to apply. + \li Right-click the transition line to open the context menu, select \uicontrol {Flow} > + \uicontrol {Flow Effects}, and then select the effect to apply. \li In \l Properties, modify the properties of the effect. \endlist - To edit effect properties later, select a transition, and then select - \uicontrol {Flow} > \uicontrol {Select Effect} in the context menu. + To edit effect properties later, select a transition, open the context menu, and then select + \uicontrol {Flow} > \uicontrol {Select Effect}. + + To use your own custom effects, select a transition, open the context menu, and then select + \uicontrol {Flow} > \uicontrol {Flow Effects} > \uicontrol {Assign Custom FlowEffect}. + Then specify the path to your custom effect file. + + To remove the current effect from a transition, select a transition, open the context menu, + and then select \uicontrol {Flow} > \uicontrol {Flow Effects} > + \uicontrol {Assign FlowEffect None}. \section1 Flow Effect Properties - You can specify basic properties for a \uicontrol {Flow Effect} - component in the \l Type and \l ID fields in the \uicontrol Component - section in the \uicontrol Properties view. + Specify basic properties for a \uicontrol {Flow Effect} component in the \l Type and + \l ID fields in the \uicontrol Component section in the \uicontrol Properties view. \image studio-flow-effect-properties.png "Flow Effect properties" - You can set the duration and easing curve of all flow effects: + Set the duration and easing curve of flow effects in the \uicontrol {Transition Effect} + section: \list \li In the \uicontrol Duration field, specify the duration of the effect. \li Select the \inlineimage icons/curve_editor.png - button to open \uicontrol {Easing Curve Editor} for attaching an + button to open \uicontrol {Easing Curve Editor} to attach an \l{Editing Easing Curves}{easing curve} to the effect. \endlist - For a move or push effect, you can set some additional properties: + Set some additional properties for a move or push effect in the \uicontrol {Push Effect} + or \uicontrol {Move Effect} section: \image studio-flow-effect-push-properties.png "Flow Push Effect properties" @@ -431,7 +443,7 @@ \li In the \uicontrol {Incoming opacity} and \uicontrol {Outgoing opacity} fields, specify the opacity of the effect as a number between 0 and 1. - \li Select the \uicontrol Reveal check box to reveal the + \li Select the \uicontrol Reveal checkbox to reveal the \uicontrol {Flow Item} where the transition starts. \endlist */ @@ -453,7 +465,7 @@ keyboard shortcuts to simulate these events when you preview the UI. When you use the wizard to create a \uicontrol {Flow View} component, select - the \uicontrol {Use event simulator} check box to add an event simulator to + the \uicontrol {Use event simulator} checkbox to add an event simulator to the flow view. You can create an event list where you assign keyboard shortcuts to events, @@ -470,9 +482,8 @@ \li In the \uicontrol {Event List} dialog, select \inlineimage icons/plus.png to add a keyboard shortcut for triggering an event to the list. \image studio-flow-event-list.png "Event List dialog" - \li In the \uicontrol {Event ID} field, enter an identifier for the - event. You can search for existing events by entering search - criteria in the \uicontrol Filter field. + \li In the \uicontrol {Event ID} field, enter an identifier for the event. To search + for existing events, enter search criteria in the \uicontrol Filter field. \li In the \uicontrol Description field, describe the keyboard shortcut. \li In the \uicontrol Shortcut field, press the keyboard key that will trigger the event, and then select \uicontrol R to record the @@ -486,24 +497,22 @@ To assign events to actions: \list 1 + \li Select \uicontrol eventListSimulator in \uicontrol Navigator, and in + \uicontrol Properties > \uicontrol {Exposed Custom Properties}, select the + \uicontrol active checkbox. If \uicontrol eventListSimulator is not visible + in the \uicontrol Navigator, select \inlineimage icons/visibilityon.png. \li In \uicontrol Navigator, select an action area or transition line. \li In the context menu, select \uicontrol {Event List} > \uicontrol {Assign Events to Actions}. - \image studio-flow-events-assign.png "Assign Events to Actions dialog" - \li In the \uicontrol ID field, select a transition or an action area - \inlineimage icons/flow-action-icon.png - . You can search for events by entering search criteria in the - \uicontrol Filter field. + \image studio-flow-events-assign.webp "Assign Events to Actions dialog" \li To connect an event, select \uicontrol Connect next to an event in the list. To release a connected event, select \uicontrol Release. \li Press \key {Alt+P} to preview the UI. \li Select action areas in the preview, double-click events in the event list, or use the keyboard shortcuts to trigger events. - \image studio-flow-decision-preview.png "Event list in preview" + \image studio-flow-events-event-list.webp "Event list in Live Preview" \endlist - If the event triggers a \l{Simulating Conditions}{flow decision}, you - can select the path to take to the next flow item. */ /*! @@ -525,17 +534,17 @@ controls, backend, or sensor data that will be required for the production version. - \image studio-flow-decision.png "Flow Decision in the 2D view" + \image studio-flow-decision.webp "Flow Decision in the 2D view" To simulate conditions: \list 1 \li Drag a \uicontrol {Flow Decision} component from - \uicontrol Components \uicontrol {Flow View} to a + \uicontrol Components > \uicontrol {Flow View} to a \l{Adding Flow Views}{flow view} in the \l Navigator or \l {2D} view. \li Select the flow item where you want the application to start in - the \uicontrol Navigator or \uicontrol {2D} view, and then select - \uicontrol {Flow} > \uicontrol {Set Flow Start} in the context menu. + the \uicontrol Navigator or \uicontrol {2D} view. Then right-click the component + to open the context menu, and select \uicontrol Flow > \uicontrol {Set Flow Start}. \li Create an \l{Adding Action Areas and Transitions}{action area} for the component that will trigger the condition and connect it to the flow decision. @@ -546,10 +555,10 @@ title for the selection dialog that opens when the condition is triggered. \li Select a transition line in the \uicontrol Navigator or - \uicontrol {2D} view and add a descriptive text in the + \uicontrol {2D} view, and add a descriptive text in the \uicontrol {Question} field in \uicontrol Properties to represent a choice in the selection dialog. - \image studio-flow-transition-properties-question.png "Flow Transition properties" + \image studio-flow-transition-properties-question.webp "Flow Transition properties" \li Press \key {Alt+P} to preview the UI. \li Select action areas in the preview, double-click events in the event list, or use the keyboard shortcuts to trigger events. @@ -558,21 +567,21 @@ Flow decisions are listed in a dialog where you can select which condition is met to see the results. - \image studio-flow-decision-preview.png "Selection dialog for flow decision" + \image studio-flow-decision-preview.webp "Selection dialog for flow decision" \section1 Flow Decision Properties - You can specify basic properties for a \uicontrol {Flow Decision} component + Specify basic properties for a \uicontrol {Flow Decision} component in the \l Type and \l ID fields in the \uicontrol Component section in the \uicontrol Properties view. Specify properties for flow decisions in the \uicontrol {Flow Decision} section. - \image studio-flow-decision-properties.png "Flow Decision properties" + \image studio-flow-decision-properties.webp "Flow Decision properties" In the \uicontrol {Dialog title} field, enter a title for the selection dialog that opens when the condition is triggered. - You can specify the following properties to change the appearance of the + Specify the following properties to change the appearance of the flow decision icon \inlineimage icons/flow-decision-icon.png : @@ -582,14 +591,14 @@ component in the \l {2D} view. \li In the \uicontrol {Label position} field, select the corner of the flow decision icon to place the label in. + \li Use the \l{Picking Colors}{color picker} to set \uicontrol {Outline color} and + \uicontrol {Fill color} of the flow decision icon. \li In the \uicontrol Size field, specify the size of the flow decision icon. \li In the \uicontrol Radius field, specify the radius of the flow decision icon corners. \endlist - You can use the \l{Picking Colors}{color picker} to set the outline and - fill color of the flow decision icon. */ /*! @@ -599,32 +608,34 @@ \title Applying States in Flows - You can use \l{Working with States}{states} in flows to modify the appearance - of \l{glossary-component}{components} in flow items in response to user - interaction, for example. For this purpose, you use the - \uicontrol {Flow Item} components available in - \uicontrol Components > \uicontrol {Flow View}. + Use \l{Working with States}{states} in flows to modify how the \l{glossary-component} + {component} properties change in flow items. For this purpose, use the \uicontrol {Flow Item} + component available in \uicontrol Components > \uicontrol {Flow View}. + + To apply states in flows: \list 1 \li Select \uicontrol File > \uicontrol {New File} > \uicontrol {Qt Quick Files} > \uicontrol {Flow Item} to create a flow item. - \li In \l States, add states to the flow item. - \li Open the .ui.qml file that contains the \l{Adding Flow Views} - {flow view} in the \l {2D} view and drag the flow item to the - flow view in the \l Navigator or \l {2D} view. - \li Drag an empty \uicontrol {Flow Item} component from - \uicontrol Components > \uicontrol {Flow View} - to the flow for each state that you added. - \li In \l Properties, in the \uicontrol {State change target} - field, select the flow item that you created using the wizard. - \image studio-flow-item-properties.png "Flow Item properties" - \li In the \uicontrol {Target state} field, select the state to - apply to the flow item. + \li In the \l States view, add states to the flow item. + \li In the \l Projects view, open the .ui.qml file that contains the \l{Adding Flow Views} + {flow view}, and drag the flow item from \uicontrol Components > + \uicontrol {My Components} to the flow view in the \l Navigator or \l 2D view. + \li From \uicontrol Components > \uicontrol {Flow View}, drag an empty + \uicontrol {Flow Item} component (1) to the flow view for each state that you added. + \image studio-flow-item-component.webp "Flow Item in the Components view." + \li In the \uicontrol Navigator or \uicontrol 2D view, select an empty + \uicontrol {Flow Item} to open the \l Properties view. + \li In the \uicontrol {State change target} field, select the flow item that you created + using the wizard. + \li In the \uicontrol {Target state} field, select the state to apply to the flow item. + \image studio-flow-states-item-properties.webp "Flow Item properties" \endlist - You can now add \l{Adding Action Areas and Transitions}{action areas} and - \l{Simulating Conditions}{flow decisions} to apply the different states. + To apply the different states to your application flow, add + \l{Adding Action Areas and Transitions}{action areas} and + \l{Simulating Conditions}{flow decisions} to the flow items. */ /*! @@ -639,11 +650,10 @@ time, based on a conditional event. For example, push notifications appear on mobile devices and incoming call screens on a car's HMI. - You can use the \uicontrol {Flow Wildcard} component to model this type of - screens in your \l{Adding Flow Views}{flow view} using real or simulated - \l{Connecting and Releasing Signals}{signals} and \l{Simulating Conditions} - {conditions}. You can add \l{Adding Flow Items}{flow items} to a positive - list to prioritize them or to a negative list to stop some screens from + Use the \uicontrol {Flow Wildcard} component to model this type of + screens in your \l{Adding Flow Views}{flow view} using real or simulated events. + Add \l{Adding Flow Items}{flow items} to an \uicontrol {Allow + list} to prioritize them or to a \uicontrol {Block list} to stop some screens from appearing on others. For example, you could block the incoming call screen from appearing on a warning screen for the engine if you consider the warning more important. @@ -652,42 +662,46 @@ \list 1 \li Drag a \uicontrol {Flow Wildcard} component from - \uicontrol Components > \uicontrol {Flow View} to an - \l{Adding Action Areas and Transitions}{action area} in - the \l Navigator or \l {2D} view. - \li In \l Properties, select flow items to add them to the - positive and negative list of the action area. + \uicontrol Components > \uicontrol {Flow View} to the \l {2D} view. + \li To connect the wildcard to a flow item with a \l{Adding Action Areas and Transitions} + {transition line}, double-click the wildcard and drag the transition line to the flow + item. + \li To add logic to the \uicontrol {Flow Wildcard} component, use + \l{Simulating Events}{events}, \l{Simulating Conditions}{Flow Decision} components, + or \l{Connecting and Releasing Signals}{signals}. + \li To manage the priority of your flow items, add flow items to + \uicontrol {Allow list} or \uicontrol {Block list} in \l Properties. + \image studio-flow-wildcard.webp "The wildcard component in 2D view." \endlist \section1 Flow Wildcard Properties - You can specify basic properties for a \uicontrol {Flow Wildcard} component + Specify basic properties for a \uicontrol {Flow Wildcard} component in the \l Type and \l ID fields in the \uicontrol Component section in the \uicontrol Properties view. Specify properties for flow wildcards in the \uicontrol {Flow Wildcard} section. - \image studio-flow-wildcard-properties.png "Flow Wildcard properties" + \image studio-flow-wildcard-properties.webp "Flow Wildcard properties" In the \uicontrol {Event IDs} field, specify the IDs of the events to connect to, such as mouse, touch or keyboard events. - Select the \uicontrol {Global wildcard} check box to enable triggering + Select the \uicontrol {Global wildcard} checkbox to enable triggering the wildcard from several flows. To give flow items high priority, select them in the \uicontrol {Allow list} field. To block flow items, select them in the \uicontrol {Block list} field. - You can specify the following properties to change the appearance of the - wildcard icon \inlineimage icons/flow-wildcard-icon.png - : + Specify the following properties to change the appearance of the + wildcard icon \inlineimage icons/flow-wildcard-icon.png: \list + \li To set the outline and fill color of the wildcard icon, use the + \l{Picking Colors}{color picker}. \li In the \uicontrol Size field, specify the size of the wildcard icon. \li In the \uicontrol Radius field, specify the radius of the wildcard icon corners. \endlist - You can use the \l{Picking Colors}{color picker} to set the outline and - fill color of the wildcard icon. */ diff --git a/doc/qtdesignstudio/src/qtdesignstudio-getting-started.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-getting-started.qdoc index 493242254f8..a975dfe7fc6 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-getting-started.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-getting-started.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 /*! @@ -56,4 +56,6 @@ If you would rather be shown things than read about them, you can watch our extensive video tutorials. \endlist + + \include qtdesignstudio-qt-academy.qdocinc qt-academy-getting-started-with-qt-design-studio */ diff --git a/doc/qtdesignstudio/src/qtdesignstudio-qt-academy.qdocinc b/doc/qtdesignstudio/src/qtdesignstudio-qt-academy.qdocinc new file mode 100644 index 00000000000..3769ddc5e34 --- /dev/null +++ b/doc/qtdesignstudio/src/qtdesignstudio-qt-academy.qdocinc @@ -0,0 +1,22 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +//! [qt-academy-using-qt-bridge-for-figma] + To learn the basics of \QBF, take the + \l {https://www.qt.io/academy/course-catalog#how-to-use-qt-bridge-for-figma} + {How to Use \QBF} course in \QAC. +//! [qt-academy-using-qt-bridge-for-figma] + +//! [qt-academy-3D-with-qt-design-studio] + To learn the basics of using 3D in \QDS, take the + \l {https://www.qt.io/academy/course-catalog#3d-with-qt-design-studio} + {3D with \QDS} course in \QAC. +//! [qt-academy-3D-with-qt-design-studio] + +//! [qt-academy-getting-started-with-qt-design-studio] + To learn the basics of using \QDS, take the + \l {https://www.qt.io/academy/course-catalog#getting-started-with-qt-design-studio} + {Getting Started with \QDS} course in \QAC. +//! [qt-academy-getting-started-with-qt-design-studio] + +*/ diff --git a/doc/qtdesignstudio/src/qtdesignstudio-terms.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-terms.qdoc index 70ff56517cb..00464f7447a 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-terms.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-terms.qdoc @@ -4,7 +4,7 @@ /*! \page studio-terms.html \previouspage studio-use-cases.html - \nextpage {Examples} + \nextpage best-practices.html \title Concepts and Terms @@ -170,7 +170,7 @@ example, if you create a 3D project, preset 3D components are added to it. You can add more preset components in \uicontrol {Components}. - \image studio-project-wizards.png "New Project dialog" + \image studio-project-wizards.webp "New Project dialog" Read more about projects: diff --git a/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc index 659e446bb38..6b3680de546 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc @@ -48,6 +48,7 @@ \li \l{Creating Projects} \li \l{Use Cases} \li \l{Concepts and Terms} + \li \l{Best Practices} \li \l{Examples} \endlist \li \l{Wireframing} @@ -74,8 +75,9 @@ \li \l{UI Controls} \li \l{Lists and Other Data Models} \li \l{2D Effects} + \li \l{Design Effects} \li \l{Logic Helpers} - \li \l Animations + \li \l{Animations} \li \l{3D Views} \li \l{Node} \li \l{Group} diff --git a/doc/qtdesignstudio/src/qtdesignstudio.qdoc b/doc/qtdesignstudio/src/qtdesignstudio.qdoc index 6aab55a1ad5..d3a0b007d12 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio.qdoc @@ -31,6 +31,7 @@ \li \l{Creating Projects} \li \l{Use Cases} \li \l{Concepts and Terms} + \li \l{Best Practices} \li \l{Examples} \endlist \li \b {\l{Wireframing}} diff --git a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-editor.qdoc b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-editor.qdoc index ebe842a5551..ff67fa14a8a 100644 --- a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-editor.qdoc +++ b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-editor.qdoc @@ -13,54 +13,28 @@ \brief Edit a 3D scene. When editing a 3D scene, you view the scene in the \uicontrol{3D} - view. You can change the projection of the view by switching between - \e {perspective camera} and \e {orthographic camera} modes. When using the - perspective camera mode, components that are far from the camera appear - smaller than those nearby. In the orthographic camera mode, all components - appear at the same scale irrespective of their distance from the camera. - Both of them are free-form camera modes that you can use to orbit around - the scene. + view. When you import 3D scenes from files that you exported from 3D graphics tools, you also import a \l{Cameras}{scene camera}, - \l{Lights}{light}, \l{3D Models}{model}, and - \l {Materials and Shaders}{materials}. If your scene did not contain - them, you can add the corresponding \l {3D Components}{Qt Quick 3D} + \l{Lights}{light}, \l{3D Models}{model}, and \l {Materials and Shaders}{materials}. + If your scene does not contain them, add the corresponding \l {3D Components}{Qt Quick 3D} components from \uicontrol Components > \inlineimage icons/plus.png > \uicontrol {Qt Quick 3D} > \uicontrol {Qt Quick 3D}. - You can use the toolbar buttons - to \e transform 3D components and manipulate the 3D scene. Transformation - refers to moving, rotating, or scaling of a component. The \e pivot of the - component is used as the origin for transformations. You can set a - \l{Managing 3D Transformations}{local pivot offset} for a component in - \uicontrol Properties to transform the component around a point other than - its local origin. A line is drawn in the \uicontrol{3D} view from the pivot - point to the center of the component to provide a visual connection between - them. Especially when working with complex scenes, it may be useful to use - the \l {Showing and Hiding Components}{showing and hiding} or the - \l {Locking Components}{locking} features in \l Navigator to avoid - transforming components by mistake while editing your scene. + Use the \uicontrol 3D toolbar buttons to modify the \uicontrol 3D view, + manipulate the 3D scene, and to access functionalities to \e transform 3D + components. Transformation refers to moving, rotating, or scaling a + component. The \e pivot of the component is used as the origin for + transformations. Set a \l{Managing 3D Transformations}{local pivot offset} + for a component in \uicontrol Properties to transform the component around + a point other than its local origin. A line is drawn in the \uicontrol{3D} + view from the pivot point to the center of the component to provide a visual + connection between them. - Toggle between local and global orientation to determine whether the gizmos - affect only the local transformations of the component or whether they - transform with respect to the global space. - - Another helpful feature when editing 3D scenes is the \e {edit light}, - which is a quick way to light the scene. - - Additionally, you can toggle the visibility of the grid, selection boxes, - icon gizmos, and camera frustums in the 3D scene. - - There is a context menu in the \uicontrol 3D view. To open it, right-click - in the \uicontrol 3D view. From the context menu you can: - \list - \li Create cameras, lights, and models. - \li Open \uicontrol {Material Editor} and edit materials. - \li Delete components - \endlist - - \image 3d-view-context-menu.webp "The context menu in the 3D view" + \note To avoid transforming components by mistake while editing your scene, + use the \l {Showing and Hiding Components}{showing and hiding} or the + \l {Locking Components}{locking} features in \l Navigator. To refresh the contents of the \uicontrol{3D} view, press \key P or select the \inlineimage icons/reset.png @@ -73,53 +47,76 @@ \youtube SsFWyUeAA_4 + \include qtdesignstudio-qt-academy.qdocinc qt-academy-3D-with-qt-design-studio + + \section1 Using the Context Menu in the 3D View + + There is a context menu in the \uicontrol 3D view. To open it, right-click + in the \uicontrol 3D view. From the context menu you can, for example: + \list + \li Create cameras, lights, and models. + \li Open \uicontrol {Material Editor} and edit materials. + \li Delete components. + \li Align camera views. + \endlist + + \image 3d-view-context-menu.webp "The context menu in the 3D view" + \section1 Controlling the 3D View Camera \section2 Toggling Camera Mode - To switch to perspective camera mode, select - \inlineimage perspective_camera.png - (\uicontrol {Toggle Perspective/Orthographic Edit Camera}). - To switch to orthographic camera mode, select - \inlineimage orthographic_camera.png - . You can also Toggle the camera mode by using the keyboard shortcut \key T. + Change the projection of the view by switching between \e {perspective camera} + and \e {orthographic camera} modes. When using the perspective camera mode, + components that are far from the camera appear smaller than those nearby. In + the orthographic camera mode, all components appear at the same scale + irrespective of their distance from the camera. Both of them are free-form + camera modes that you can use to orbit around the scene. + + To toggle the camera mode, do one of the following: + \list + \li Select \inlineimage perspective_camera.png + (\uicontrol {Toggle Perspective/Orthographic Camera}) to use the + perspective camera mode. + \li Select \inlineimage orthographic_camera.png to use the orthographic + camera mode. + \endlist + You can also toggle the camera mode by using the keyboard shortcut \key T. \section2 Navigating in the 3D Scene - You can navigate the scene by panning, rotating, and zooming the 3D view + Navigate the scene by panning, rotating, and zooming the 3D view camera: \list \li To pan, press \key Alt (or \key Option on \macos) and use the - middle mouse button to click and drag anywhere in the rendered - view to slide the view around. Alternatively, press and hold \key {right mouse - button} and \key {left mouse button} and drag anywhere in the view to pan. - \li To orbit, press \key Alt and click and drag anywhere in the rendered - view to rotate the view. + wheel button to drag anywhere in the \uicontrol 3D view to + slide the view around. Alternatively, press and hold \key + {right mouse button} and \key {left mouse button} and drag + anywhere in the view to pan. + \li To orbit, press \key Alt and and drag anywhere in the + \uicontrol 3D view to rotate the view. \li To zoom, use the mouse wheel or press \key Alt and right-click - anywhere in the rendered view to zoom the view in or out as you drag + anywhere in the \uicontrol 3D view to zoom the view in or out as you drag up or down. \endlist - To zoom and focus the 3D view camera on a selected component, - select \inlineimage fit_selected.png - (\uicontrol {Fit Selected}) or press \key F. + To zoom and focus the 3D view camera on a selected component, select + \inlineimage fit_selected.png (\uicontrol {Fit Selected}) or press \key F. The world axis helper (1) shows the direction of the world axes in the view. - To point the camera at the currently selected component in the direction of + To point the 3D view camera at the currently selected component in the direction of an axis, click the axis. Clicking the dot at the end of the axis will point the camera at the opposite direction of the axis. If no component is - selected, the camera is pointed at the world origin. This does not affect - the camera zoom level. + selected, the camera is pointed at the world origin. \image studio-3d-editor-axis-helper.webp "Axis helper in the 3D view" - You can use scene cameras (2) to view the \uicontrol View3D component from a - specific angle in the \l {2D} view while editing scenes. Different types of - cameras are available in \uicontrol Components - > \uicontrol {Qt Quick 3D} > \uicontrol {Qt Quick 3D}. For more information - about using cameras in the scene, the available camera types, and their - properties, see \l{Cameras}. + Use scene cameras (2) to view the \uicontrol View3D component from a + specific angle in the \l {2D} view while editing scenes. Drag a camera + component to your scene from \uicontrol Components > \uicontrol {Qt Quick 3D} > + \uicontrol {Qt Quick 3D}. For more information about using cameras in the + scene and the available camera types, see \l{Cameras}. \section2 Using Split View @@ -128,16 +125,16 @@ \image studio-3d-split-view.webp "Split view in the 3D view" - To select one of the four panes, click on it. The selected pane is marked with + To select one of the four panes, click the pane. The selected pane is marked with a blue frame. Use the world axis helper to change the point of view for each pane independently. Navigate each split by panning, rotating, and zooming, as described above. \section2 Using Fly Mode - You can move around freely in the 3D scene with fly mode. To navigate the scene with - fly mode, keep the \key {right mouse button} pressed and use the listed keys to move - around the scene. + Use the fly mode to move around freely in the 3D scene. To navigate the scene with + fly mode, right-click and hold in the \uicontrol 3D view, and use the keyboard shortcuts + to move around the scene: \table \header @@ -161,10 +158,20 @@ \row \li \key Q or \key {Page down} \li Move down. + \row + \li \key Shift + \li Move faster. + \row + \li \key Alt + \li Move slower. + \row + \li \key Spacebar + \li Move to an object in the crosshairs. \endtable - To adjust the movement speed, select \inlineimage icons/camera_speed.png in the - \uicontrol 3D view toolbar to open the configuration dialog. + To adjust the movement speed, right-click and hold in the \uicontrol 3D view, and rotate the + mouse wheel. Alternatively, select \inlineimage icons/camera_speed.png in the \uicontrol 3D + view toolbar to open the configuration dialog. In the configuration dialog, you can: \list @@ -174,10 +181,8 @@ \section1 Using Global and Local Orientation - To switch between local and global orientation, select - \inlineimage global.png - (\uicontrol {Toggle Local/Global Orientation}) - or press \key Y. + In \uicontrol 3D view, you view the scene in global or local orientation + mode. In global orientation mode, transformation of a selected component is presented with respect to the global space. For example, while the move tool @@ -191,40 +196,40 @@ the axes of global space. Dragging on the red arrow of the gizmo moves the component in the local x direction in relation to the component. + To switch between local and global orientation, select \inlineimage global.png + (\uicontrol {Toggle Local/Global Orientation}) or press \key Y. + \section1 Using Edit Light The edit light is an extra point light that can be used to illuminate the scene. To toggle the edit light on and off, select \inlineimage edit_light_on.png - or \inlineimage edit_light_off.png - (\uicontrol {Toggle Edit Light}) - or press \key U. + or \inlineimage edit_light_off.png (\uicontrol {Toggle Edit Light}) or + press \key U. - For more information about the available scene light types and their - properties, see \l{Lights}. + For information about the available scene light types and their properties, + see \l{Lights}. \section1 Baking Lights - Bake lights to light static elements in your scene. To bake lights, - select \inlineimage icons/bakelights.png to open the - \uicontrol {Lights Baking Setup} dialog. For more information, see - \l {Baking Lightmaps}. + Bake lights to light static elements in your scene. To bake lights, select + \inlineimage icons/bakelights.png to open the \uicontrol {Lights Baking Setup} + dialog. For more information, see \l {Baking Lightmaps}. \section1 Selecting Components To move, rotate, or scale components in the scene, you need to select them - first. The selection mode buttons determine how components are selected when - you click them in the \uicontrol{3D} view: + first. Toggle the selection mode to determine how components are selected + when you click them in the \uicontrol{3D} view: \list - \li In the \inlineimage select_item.png - (\uicontrol {Single Selection}) mode, a single component is selected. - \li In the \inlineimage select_group.png - (\uicontrol {Group Selection}) mode, the top level parent of the - component is selected. This enables you to move, rotate, or scale a - group of components. + \li Use the \inlineimage select_item.png (\uicontrol {Single Selection}) + mode to select a single component. + \li Use the \inlineimage select_group.png (\uicontrol {Group Selection}) + mode to select the top level parent of the component, so you can move + a group of components simultaneously. \endlist - To toggle the selection mode, press \key Q. + Alternatively, press \key Q to toggle the selection mode. To multiselect, hold \key Ctrl and click the components you wish to select. @@ -241,16 +246,15 @@ y, or z axis or on the top, bottom, left, and right clip planes of the the \uicontrol{3D} view. - To move components, select \inlineimage move_off.png - or press \key W: + To move components, select \inlineimage move_off.png or press \key W: \list \li To move components along the axes of the move gizmo, click the axis, and drag the component along the axis. - \li To move components on a plane, click the plane handle and drag the + \li To move components on a plane, drag the plane handle of the move component on the plane. - \li To move components freely in the 3D view, click and drag the gray - handle at the center of the move gizmo. + \li To move components freely in the 3D view, drag the gray handle at the + center of the move gizmo. \endlist \section1 Rotating Components @@ -261,29 +265,28 @@ or press \key E: \list - \li To rotate a component around its rotation gizmo, click the axis ring - and drag in the direction you want to rotate the component in. - \li To freely rotate the component, click and drag the inner center - circle of the gizmo. + \li To rotate a component around its rotation gizmo, drag the axis ring + in the direction you want to rotate the component in. + \li To freely rotate the component, drag the inner center circle of the + gizmo. \endlist \section1 Scaling Components \image studio-3d-editor-scale.webp "The 3D view in scale mode" - You can use the scale handles to adjust the local x, y, or z scale of a + Úse the scale handles to adjust the local x, y, or z scale of a component. You can adjust the scale across one, two, or three axes, depending on the handle. - To scale components, select \inlineimage scale_off.png - or press \key R: + To scale components, select \inlineimage scale_off.png or press \key R: \list - \li To adjust the scale across one axis, click and drag the scale handle + \li To adjust the scale across one axis, drag the scale handle attached to the axis. - \li To adjust the scale across a plane, click the plane handle and drag - the component on the plane. - \li To uniformly scale a component across all axes, click and drag the + \li To adjust the scale across a plane, drag the plane handle of + the component. + \li To uniformly scale a component across all axes, drag the gray handle at the center of the component. \endlist @@ -292,7 +295,7 @@ With snapping turned on, the objects in the \uicontrol 3D view snap to certain intervals during transformation (move, rotate, scale). - You can toggle snapping in the following ways: + Toggle snapping in the following ways: \list \li Select \inlineimage icons/snapping-3d.png @@ -300,7 +303,7 @@ \li Hold down the \key Ctrl key. \endlist - With snapping turned on, you can press and hold \key Shift to snap objects to one tenth of + With snapping turned on, press and hold \key Shift to snap objects to one tenth of the specified snap interval. \section2 Configuring Snapping @@ -327,78 +330,85 @@ To align a camera to the \uicontrol{3D} view: \list 1 - \li Select a camera in the \uicontrol{3D} or \uicontrol {Navigator} view. + \li Select a scene camera in the \uicontrol{3D} or \uicontrol {Navigator} view. \note If you don't have a camera selected, the most recently selected camera is aligned to the view. - \li In the \uicontrol{3D} view, - select \inlineimage icons/align-camera-on.png - . + \li In the \uicontrol{3D} view, select \inlineimage icons/align-camera-on.png. \endlist - This moves and rotates the camera so that the camera shows the same view - as the current view in the \uicontrol{3D} view. + This moves and rotates the scene camera to show the same view as the current view + in the \uicontrol{3D} view. To align the \uicontrol{3D} view to a camera: - \list 1 - \li Select a camera in the \uicontrol{3D} view or \uicontrol {Navigator}. + \list 1 + \li Select a scene camera in the \uicontrol{3D} view or \uicontrol {Navigator}. \note If you don't have a camera selected, the view is aligned to the most recently - selected camera. - \li In the \uicontrol{3D} view, - select \inlineimage icons/align-view-on.png - . + selected camera. + \li In the \uicontrol{3D} view, select \inlineimage icons/align-view-on.png. \endlist - This copies the position as well as x and y rotation values from the - camera and applies them to the \uicontrol{3D} view. + This moves and rotates the 3D view to show the same view as the selected scene camera. \section1 Toggling Visibility To toggle the visibility of objects in the \uicontrol{3D} view, select - \inlineimage icons/visibilityon.png - in the toolbar. This opens a menu with the following options: + \inlineimage icons/visibilityon.png in the toolbar. This opens a menu with the + following options: \table - \row - \li Show Grid - \li Toggles the visibility of the helper grid. - \row - \li Show Selection Boxes - \li Toggles the visibility of selection boxes for selected 3D objects. - \row - \li Show Icon Gizmos - \li Toggles the visibility of icon gizmos for object such as cameras, - lights, and particle systems. - \row - \li Always Show Camera Frustums - \li Toggles between always showing the camera frustum and showing it - only for cameras selected in the \uicontrol{3D} view. - \row - \li Always Show Particle Emitters and Attractors - \li Toggle between always showing the particle emitter and attractor - visualizations and only showing them when the emitter or attractor is - selected in the \uicontrol{3D} view. + \header + \li Action + \li Description + \li Keyboard Shortcut + \row + \li Show Grid + \li Toggles the visibility of the helper grid. + \li \key G + \row + \li Show Selection Boxes + \li Toggles the visibility of selection boxes for selected 3D objects. + \li \key B + \row + \li Show Icon Gizmos + \li Toggles the visibility of icon gizmos for object such as cameras, + lights, and particle systems. + \li \key I + \row + \li Always Show Camera Frustums + \li Toggles between always showing the camera frustum and showing it + only for cameras selected in the \uicontrol{3D} view. + \li \key C + \row + \li Always Show Particle Emitters and Attractors + \li Toggles between always showing the particle emitter and attractor + visualizations and only showing them when the emitter or attractor + is selected in the \uicontrol{3D} view. + \li \key M \endtable \section1 Changing Colors To change the \uicontrol 3D view background or grid color, select - \inlineimage icons/3d-background-color.png - in the toolbar. This opens a menu with the following options: + \inlineimage icons/3d-background-color.png in the toolbar. This opens a menu + with the following options: \table + \header + \li Action + \li Decription + \row + \li Select Background Color + \li Select a color for the background. \row - \li Select Background Color - \li Select a color for the background. + \li Select Grid Color + \li Select a color for the grid. \row - \li Select Grid Color - \li Select a color for the grid. + \li Use Scene Environment Color + \li Sets the 3D view to use the scene environment color as background + color. \row - \li Use Scene Environment Color - \li Sets the 3D view to use the scene environment color as background - color. - \row - \li Reset Colors - \li Resets the background and grid colors to the default colors. + \li Reset Colors + \li Resets the background and grid colors to the default colors. \endtable \section1 Particle Editor @@ -413,16 +423,14 @@ \li Select a particle system in the \uicontrol Navigator or \uicontrol{3D} view. \li In the \uicontrol{3D} view, select - \inlineimage icons/particle-animation-on.png - to activate particle animation. Now you can see the particle animation in - the \uicontrol{3D} view. + \inlineimage icons/particle-animation-on.png to activate particle animation. + Now you can see the particle animation in the \uicontrol{3D} view. \endlist You can pause the particle animation by selecting - \inlineimage icons/particle-pause.png - . When the animation is paused, you can use - \inlineimage icons/particles-seek.png - to manually seek forward or backward in the particle animation. + \inlineimage icons/particle-pause.png. When the animation is paused, use + \inlineimage icons/particles-seek.png to manually seek forward or backward in + the particle animation. \section1 Using Viewport Shading diff --git a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-repeater-3d.qdoc b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-repeater-3d.qdoc index 0f0d0634cab..5cf2370cc98 100644 --- a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-repeater-3d.qdoc +++ b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-repeater-3d.qdoc @@ -142,7 +142,7 @@ \li From \uicontrol Components, drag a \uicontrol Sphere to \e _3DRepeater in \uicontrol Navigator. \image repeater3d-listmodel-navigator.png - \li Select \e sphere in \uicontrol Navigator and in the \Properties view, select + \li Select \e sphere in \uicontrol Navigator and in the \uicontrol Properties view, select \inlineimage icons/action-icon.png next to \uicontrol Scale > \uicontrol X. \li Select \uicontrol {Set binding} to open \uicontrol {Binding Editor}. diff --git a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-view.qdoc b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-view.qdoc index 1ada3625ff3..db9961e31d7 100644 --- a/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-view.qdoc +++ b/doc/qtdesignstudio/src/qtquick3d-editor/qtdesignstudio-3d-view.qdoc @@ -17,7 +17,7 @@ To create a project with many complex effects, use the \uicontrol {Extended 3D} preset that creates a project with an \uicontrol {Extended View3D} component. - The extended 3D view includes an {Extended Scene Environment} + The extended 3D view includes an \uicontrol{Extended Scene Environment} component that enables using various effects by defining them as properties. \note The extended 3D view is available in projects created with Qt 6.5 diff --git a/doc/qtdesignstudio/src/qtquickdesigner-components/qtdesignstudio-logic-helpers.qdoc b/doc/qtdesignstudio/src/qtquickdesigner-components/qtdesignstudio-logic-helpers.qdoc index c928fff58c6..72f57f0c174 100644 --- a/doc/qtdesignstudio/src/qtquickdesigner-components/qtdesignstudio-logic-helpers.qdoc +++ b/doc/qtdesignstudio/src/qtquickdesigner-components/qtdesignstudio-logic-helpers.qdoc @@ -3,7 +3,7 @@ /*! \page quick-logic-helpers.html - \previouspage quick-2d-effects.html + \previouspage quick-design-effects.html \nextpage quick-animations.html \title Logic Helpers diff --git a/doc/qtdesignstudio/src/qtquickdesigner-components/qtdesignstudio-property-visual-effects.qdoc b/doc/qtdesignstudio/src/qtquickdesigner-components/qtdesignstudio-property-visual-effects.qdoc new file mode 100644 index 00000000000..3530d4da645 --- /dev/null +++ b/doc/qtdesignstudio/src/qtquickdesigner-components/qtdesignstudio-property-visual-effects.qdoc @@ -0,0 +1,136 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +/*! + \page quick-design-effects.html + \previouspage quick-2d-effects.html + \nextpage quick-logic-helpers.html + + \title Design Effects + + \QDS provides a set of effects in the \uicontrol Properties view that you can apply + to the components. + + \image studio-effects.webp "Design Effects in the Properties view" + + \note This feature is a \e Beta release. + + The available set of Design Effects applies to the \QDS components: + \list + \li \l {Applying Layer Blur on a Component} {Layer Blur} + \li \l {Applying Background Blur on a Component} {Background Blur} + \li \l {Applying Drop Shadow on a Component} {Drop Shadow} + \li \l {Applying Inner Shadow on a Component} {Inner Shadow} + \endlist + + \section1 Applying Layer Blur on a Component + + Use \uicontrol {Layer Blur} to make a component blurry. To apply \uicontrol {Layer Blur} + to a component, follow these steps: + \list 1 + \li Select the component in the \uicontrol 2D or \uicontrol Navigator view. + \li Go to the \uicontrol {Properties} view > \uicontrol Effects + and select \uicontrol {Add Effects}. + \li Go to \uicontrol {Layer Blur} and enter the level of blurring you need + in \uicontrol {Blur}. + + \image studio-effects-layer-blur.webp "Layer Blur Effects in Properties view" + \endlist + \note The level of \uicontrol {Layer Blur} is adjustable between zero and one hundred. \br + To remove the applied \uicontrol {Layer Blur}, select the component, + then go to \uicontrol {Properties} view > \uicontrol {Layer Blur} + > \uicontrol {Remove Effects}. This also removes all the other effects + applied to the component. + + \section1 Applying Background Blur on a Component + + Apply \uicontrol {Background Blur} to a component when you want to blur a selected + component behind it. There are a few essential conditions you should consider. + \list + \li Make the component partially transparent. With solid color, + the background component is not visible. + \li Use a solid color on the background component. On + a transparent background component the \uicontrol {Background Blur} does not + function properly. + \note Currently, the \uicontrol {Background Blur} functions on top of only one selected + background component. All the other components ignore the blurring. + \endlist + + After fulfilling the above conditions, follow the next steps to apply the + \uicontrol {Background Blur} on a component. + + \list 1 + \li Select the component in the \uicontrol 2D or \uicontrol Navigator view. + \li Go to the \uicontrol {Properties} view > \uicontrol Effects + and select \uicontrol {Add Effects}. + \li Go to \uicontrol {Background Blur} and enter the level of blurring you need + in \uicontrol {Blur}. + \li In the \uicontrol Background dropdown menu, select another + components as the background component. + \li Drag the component on top of the background component. The area from the + component covering the component selected as \uicontrol Background gets + blurred. However, any other component behind the component doesn't blur. + \endlist + + \image studio-effects-background-blur.webp "Applying Background Blur" + + \section1 Applying Drop Shadow on a Component + + Shadows can either fall outside or inside the component. + The shadow that falls outside is a drop shadow. To apply + a \uicontrol {Drop Shadow} to a component, follow the instructions below. + + \list 1 + \li Select the component in the \uicontrol 2D or \uicontrol Navigator view. + \li Go to the \uicontrol {Properties} view > \uicontrol Effects + and select \uicontrol {Add Effects}. + \endlist + + This adds the default drop shadow to the component. To adjust this shadow, + follow these instructions. + + \list 1 + \li Select the component and go to the \uicontrol Properties view > \uicontrol Effects. + Then, select \inlineimage icons/particle-play.png next to the shadow type + selector dropdown menu. + \li Adjust \uicontrol Blur, \uicontrol Offset, \uicontrol Spread, and \uicontrol Color + to shape the shadow. + \endlist + + \image studio-effects-drop-shadow.webp "Drop Shadow Effects in Properties view" + + \note To stack multiple shadows, select \uicontrol {Add Shadow Effect} from the + \uicontrol Properties view. After adding multiple shadows, you can adjust them + separately from their expandable menu. + + The \uicontrol {Show behind} feature in \uicontrol {Drop Shadow} only works when + the component is transparent. To use it: + + \list 1 + \li Select the component with a drop shadow. + \li Go to the \uicontrol Properties view > \uicontrol Effects. + Then, select \inlineimage icons/particle-play.png next to the shadow type + selector dropdown menu. + \li Select \uicontrol {Show Behind}. + + \image studio-effects-show-shadow-behind.webp "Show drop shadow behind the component" + \endlist + + \section1 Applying Inner Shadow on a Component + + \uicontrol {Inner shadow} works inside a component. To apply \uicontrol {Inner Shadow}, + do the following: + + \list 1 + \li Select the component in the \uicontrol 2D or \uicontrol Navigator view. + \li Go to the \uicontrol {Properties} view > \uicontrol Effects + and select \uicontrol {Add Effects}. + \li From the dropdown menu, select \uicontrol {Inner Shadow}. + \li Adjust \uicontrol Blur, \uicontrol Offset, \uicontrol Spread, and \uicontrol Color + to shape the shadow. + + \image studio-effects-inner-shadow.webp "Inner shadow of the component" + \endlist + + +*/ diff --git a/doc/qtdesignstudio/src/qtquickdesigner-components/qtdesignstudio-visual-effects.qdoc b/doc/qtdesignstudio/src/qtquickdesigner-components/qtdesignstudio-visual-effects.qdoc index 9cff12750c0..52ef110a626 100644 --- a/doc/qtdesignstudio/src/qtquickdesigner-components/qtdesignstudio-visual-effects.qdoc +++ b/doc/qtdesignstudio/src/qtquickdesigner-components/qtdesignstudio-visual-effects.qdoc @@ -4,7 +4,7 @@ /*! \page quick-2d-effects.html \previouspage quick-data-models.html - \nextpage quick-logic-helpers.html + \nextpage quick-property-effects.html \title 2D Effects diff --git a/doc/qtdesignstudio/src/views/studio-model-editor.qdoc b/doc/qtdesignstudio/src/views/studio-model-editor.qdoc index 4d10f87552e..0caebcfb493 100644 --- a/doc/qtdesignstudio/src/views/studio-model-editor.qdoc +++ b/doc/qtdesignstudio/src/views/studio-model-editor.qdoc @@ -18,7 +18,7 @@ \image edit-list-model-model-editor.webp For examples of how to use data models, see - \l {Adding a Repeater3D Component with a List Model}. + \l {Adding a Repeater3D Component with a Model}. \section1 Creating a Data Model diff --git a/share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetsContextMenu.qml b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetsContextMenu.qml index a8eb5285f57..fe22d7ce51f 100644 --- a/share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetsContextMenu.qml +++ b/share/qtcreator/qmldesigner/assetsLibraryQmlSources/AssetsContextMenu.qml @@ -249,7 +249,7 @@ StudioControls.Menu { StudioControls.MenuItem { text: qsTr("Add to Content Library") - visible: root.rootView.userBundleEnabled() && root.__fileIndex && root.assetsModel.allFilePathsAreTextures(root.__selectedAssetPathsList) + visible: root.__fileIndex && root.assetsModel.allFilePathsAreTextures(root.__selectedAssetPathsList) height: visible ? implicitHeight : 0 onTriggered: root.rootView.addAssetsToContentLibrary(root.__selectedAssetPathsList) } diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsEditDelegate.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsEditDelegate.qml deleted file mode 100644 index fd969382e63..00000000000 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsEditDelegate.qml +++ /dev/null @@ -1,247 +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 CollectionDetails 1.0 as CollectionDetails -import StudioControls 1.0 as StudioControls -import StudioHelpers as StudioHelpers -import StudioTheme 1.0 as StudioTheme -import QtQuick.Templates as T - -Item { - id: root - - required property var columnType - - TableView.onCommit: { - if (editorLoader.changesAccepted && edit !== editorLoader.acceptedValue) - edit = editorLoader.acceptedValue - } - - onActiveFocusChanged: { - if (root.activeFocus && !editorLoader.triggered && editorLoader.item) { - editorLoader.triggered = true - editorLoader.item.open() - } - - // active focus should be checked again, because it might be affected by editorLoader.item - if (root.activeFocus && editorLoader.editor) - editorLoader.editor.forceActiveFocus() - } - - Loader { - id: editorLoader - - active: true - - property var editor: editorLoader.item ? editorLoader.item.editor : null - property var editValue: editorLoader.editor ? editorLoader.editor.editValue : null - property var acceptedValue: null - property bool changesAccepted: true - property bool triggered: false - - Connections { - id: modifierFocusConnection - - target: editorLoader.editor - enabled: editorLoader.item !== undefined - - function onActiveFocusChanged() { - if (!modifierFocusConnection.target.activeFocus) { - editorLoader.acceptedValue = editorLoader.editValue - root.TableView.commit() - } - } - } - - Component { - id: textEditor - - EditorPopup { - editor: textField - - StudioControls.TextField { - id: textField - - property alias editValue: textField.text - - actionIndicator.visible: false - translationIndicatorVisible: false - - onRejected: editorLoader.changesAccepted = false - } - } - } - - Component { - id: realEditor - - EditorPopup { - - editor: realField - - StudioControls.RealSpinBox { - id: realField - - property alias editValue: realField.realValue - - actionIndicator.visible: false - realFrom: -9e9 - realTo: 9e9 - realStepSize: 1.0 - decimals: 6 - trailingZeroes: false - - onActiveFocusChanged: { - if (realField.activeFocus) - realField.contentItem.focus = true - } - - textFromValue: function (value, locale) { - locale.numberOptions = Locale.OmitGroupSeparator - var decimals = realField.trailingZeroes ? realField.decimals : decimalCounter(realField.realValue) - if (decimals > 0) { - var text = Number(realField.realValue).toLocaleString(locale, 'f', decimals + 1) - return text.substring(0, text.length - 1) - } - return Number(realField.realValue).toLocaleString(locale, 'f', decimals) - } - } - } - } - - Component { - id: integerEditor - - EditorPopup { - - editor: integerField - - StudioControls.SpinBox { - id: integerField - - property alias editValue: integerField.value - - actionIndicatorVisible: false - spinBoxIndicatorVisible: true - from: -2147483647 - to: 2147483647 - decimals: 0 - - onActiveFocusChanged: { - if (integerField.activeFocus) - integerField.contentItem.focus = true - } - } - } - } - - Component { - id: boolEditor - - EditorPopup { - - editor: boolField - - StudioControls.CheckBox { - id: boolField - - property alias editValue: boolField.checked - - actionIndicatorVisible: false - } - } - } - } - - component EditorPopup: T.Popup { - id: editorPopup - - required property Item editor - - implicitHeight: contentHeight - implicitWidth: contentWidth - - focus: true - visible: false - - Connections { - target: editorPopup.editor - - function onActiveFocusChanged() { - if (!editorPopup.editor.activeFocus) - editorPopup.close() - else if (edit) - editorPopup.editor.editValue = edit - } - } - - Connections { - target: editorPopup.editor.Keys - - function onEscapePressed() { - editorLoader.changesAccepted = false - editorPopup.close() - } - - function onReturnPressed() { - editorPopup.close() - } - - function onEnterPressed() { - editorPopup.close() - } - } - } - - states: [ - State { - name: "default" - when: columnType !== CollectionDetails.DataType.Boolean - && columnType !== CollectionDetails.DataType.Color - && columnType !== CollectionDetails.DataType.Integer - && columnType !== CollectionDetails.DataType.Real - - PropertyChanges { - target: editorLoader - sourceComponent: textEditor - } - }, - State { - name: "integer" - when: columnType === CollectionDetails.DataType.Integer - - PropertyChanges { - target: editorLoader - sourceComponent: integerEditor - } - }, - State { - name: "real" - when: columnType === CollectionDetails.DataType.Real - - PropertyChanges { - target: editorLoader - sourceComponent: realEditor - } - }, - State { - name: "bool" - when: columnType === CollectionDetails.DataType.Boolean - - PropertyChanges { - target: editorLoader - sourceComponent: boolEditor - } - }, - State { - name: "color" - when: columnType === CollectionDetails.DataType.Color - - PropertyChanges { - target: editorLoader - sourceComponent: null - } - } - ] -} diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml deleted file mode 100644 index e7997f7eae6..00000000000 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml +++ /dev/null @@ -1,291 +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 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 { - id: root - - required property var model - required property var backend - property int selectedRow: -1 - - implicitHeight: StudioTheme.Values.toolbarHeight - color: StudioTheme.Values.themeToolbarBackground - - function addNewColumn() { - addColumnDialog.popUp(root.model.columnCount()) - } - - function addNewRow() { - root.model.insertRow(root.model.rowCount()) - } - - function closeDialogs() { - addColumnDialog.reject() - fileDialog.reject() - } - - RowLayout { - id: container - - anchors.fill: parent - anchors.topMargin: StudioTheme.Values.toolbarVerticalMargin - anchors.bottomMargin: StudioTheme.Values.toolbarVerticalMargin - - spacing: StudioTheme.Values.sectionRowSpacing - - RowLayout { - id: leftSideToolbar - - Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter - Layout.leftMargin: StudioTheme.Values.toolbarHorizontalMargin - spacing: StudioTheme.Values.sectionRowSpacing - - IconButton { - id: addColumnLeftButton - - buttonIcon: StudioTheme.Constants.addcolumnleft_medium - tooltip: qsTr("Add column left") - enabled: root.model.selectedColumn > -1 - onClicked: addColumnDialog.popUp(root.model.selectedColumn) - } - - IconButton { - id: addColumnRightButton - - buttonIcon: StudioTheme.Constants.addcolumnright_medium - tooltip: qsTr("Add column right") - enabled: root.model.selectedColumn > -1 - onClicked: addColumnDialog.popUp(root.model.selectedColumn + 1) - } - - IconButton { - id: deleteColumnButton - - buttonIcon: StudioTheme.Constants.deletecolumn_medium - tooltip: qsTr("Delete selected column") - enabled: root.model.selectedColumn > -1 - onClicked: root.model.removeColumn(root.model.selectedColumn) - } - - Item { // spacer - implicitWidth: StudioTheme.Values.toolbarSpacing - implicitHeight: 1 - } - - IconButton { - id: addRowBelowButton - - buttonIcon: StudioTheme.Constants.addrowbelow_medium - tooltip: qsTr("Add row below") - enabled: root.model.selectedRow > -1 - onClicked: root.model.insertRow(root.model.selectedRow + 1) - } - - IconButton { - id: addRowAboveButton - - buttonIcon: StudioTheme.Constants.addrowabove_medium - tooltip: qsTr("Add row above") - enabled: root.model.selectedRow > -1 - onClicked: root.model.insertRow(root.model.selectedRow) - } - - IconButton { - id: deleteSelectedRowButton - - buttonIcon: StudioTheme.Constants.deleterow_medium - tooltip: qsTr("Delete selected row") - enabled: root.model.selectedRow > -1 - onClicked: root.model.removeRow(root.model.selectedRow) - } - } - - RowLayout { - id: rightSideToolbar - - spacing: StudioTheme.Values.sectionRowSpacing - Layout.alignment: Qt.AlignRight | Qt.AlignVCenter - Layout.rightMargin: StudioTheme.Values.toolbarHorizontalMargin - - IconButton { - id: saveCollectionButton - - buttonIcon: StudioTheme.Constants.save_medium - tooltip: qsTr("Save changes") - enabled: root.model.collectionName !== "" && root.model.hasUnsavedChanges - onClicked: root.model.saveDataStoreCollections() - - Rectangle { - width: StudioTheme.Values.smallStatusIndicatorDiameter - height: StudioTheme.Values.smallStatusIndicatorDiameter - radius: StudioTheme.Values.smallStatusIndicatorDiameter / 2 - anchors.right: parent.right - anchors.top: parent.top - visible: root.model.hasUnsavedChanges - color: StudioTheme.Values.themeIconColorSelected - } - } - - IconButton { - id: exportCollectionButton - - buttonIcon: StudioTheme.Constants.export_medium - tooltip: qsTr("Export model") - enabled: root.model.collectionName !== "" - onClicked: fileDialog.open() - } - } - } - - PlatformWidgets.FileDialog { - id: fileDialog - - fileMode: PlatformWidgets.FileDialog.SaveFile - - nameFilters: ["JSON Files (*.json)", - "Comma-Separated Values (*.csv)" - ] - - selectedNameFilter.index: 0 - - onAccepted: { - let filePath = fileDialog.file.toString() - root.model.exportCollection(filePath) - } - } - - component IconButton: HelperWidgets.AbstractButton { - style: StudioTheme.Values.viewBarButtonStyle - } - - component Spacer: Item { - implicitWidth: 1 - implicitHeight: StudioTheme.Values.columnGap - } - - RegularExpressionValidator { - id: nameValidator - regularExpression: /^\w+$/ - } - - StudioControls.Dialog { - id: addColumnDialog - - property int clickedIndex: -1 - property bool nameIsValid - - title: qsTr("Add Column") - - function popUp(index) - { - addColumnDialog.clickedIndex = index - columnName.text = "" - columnName.forceActiveFocus() - addedPropertyType.currentIndex = addedPropertyType.find("String") - - addColumnDialog.open() - } - - function addColumnName() { - if (addColumnDialog.nameIsValid) { - root.model.addColumn(addColumnDialog.clickedIndex, columnName.text, addedPropertyType.currentText) - addColumnDialog.accept() - } else { - addColumnDialog.reject() - } - } - - contentItem: ColumnLayout { - spacing: 2 - - Text { - text: qsTr("Column name:") - color: StudioTheme.Values.themeTextColor - } - - StudioControls.TextField { - id: columnName - - Layout.fillWidth: true - - actionIndicator.visible: false - translationIndicator.visible: false - validator: nameValidator - - Keys.onEnterPressed: addColumnDialog.addColumnName() - Keys.onReturnPressed: addColumnDialog.addColumnName() - Keys.onEscapePressed: addColumnDialog.reject() - - onTextChanged: { - addColumnDialog.nameIsValid = (columnName.text !== "" - && !root.model.isPropertyAvailable(columnName.text)) - } - } - - Spacer { implicitHeight: StudioTheme.Values.controlLabelGap } - - Label { - Layout.fillWidth: true - - text: qsTr("The model already contains \"%1\"!").arg(columnName.text) - visible: columnName.text !== "" && !addColumnDialog.nameIsValid - - color: StudioTheme.Values.themeTextColor - wrapMode: Label.WordWrap - padding: 5 - - background: Rectangle { - color: "transparent" - border.width: StudioTheme.Values.border - border.color: StudioTheme.Values.themeWarning - } - } - - Spacer {} - - Text { - text: qsTr("Type:") - color: StudioTheme.Values.themeTextColor - } - - StudioControls.ComboBox { - id: addedPropertyType - - Layout.fillWidth: true - - model: CollectionDataTypeModel{} - textRole: "display" - tooltipRole: "toolTip" - actionIndicatorVisible: false - } - - Spacer {} - - RowLayout { - Layout.alignment: Qt.AlignRight | Qt.AlignVCenter - spacing: StudioTheme.Values.sectionRowSpacing - - HelperWidgets.Button { - enabled: addColumnDialog.nameIsValid - text: qsTr("Add") - onClicked: addColumnDialog.addColumnName() - } - - HelperWidgets.Button { - text: qsTr("Cancel") - onClicked: addColumnDialog.reject() - } - } - } - } -} diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml deleted file mode 100644 index 2193bd1763e..00000000000 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml +++ /dev/null @@ -1,727 +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 CollectionDetails 1.0 as CollectionDetails -import HelperWidgets 2.0 as HelperWidgets -import StudioControls 1.0 as StudioControls -import StudioTheme 1.0 as StudioTheme - -Rectangle { - id: root - - required property var model - required property var backend - required property var sortedModel - - implicitWidth: 300 - implicitHeight: 400 - color: StudioTheme.Values.themeControlBackground - - function closeDialogs() { - editPropertyDialog.reject() - deleteColumnDialog.reject() - toolbar.closeDialogs() - } - - MouseArea { - anchors.fill: parent - onClicked: tableView.model.deselectAll() - } - - Column { - id: topRow - readonly property real maxAvailableHeight: root.height - - visible: root.model.collectionName !== "" - width: parent.width - spacing: 10 - - CollectionDetailsToolbar { - id: toolbar - model: root.model - backend: root.backend - width: parent.width - } - - GridLayout { - id: gridLayout - readonly property real maxAvailableHeight: topRow.maxAvailableHeight - - topRow.spacing - - toolbar.height - - columns: 3 - rowSpacing: 1 - columnSpacing: 1 - width: parent.width - - anchors { - left: parent.left - leftMargin: StudioTheme.Values.collectionTableHorizontalMargin - } - - Rectangle { - id: tableTopLeftCorner - - clip: true - visible: !tableView.model.isEmpty - color: StudioTheme.Values.themeControlBackgroundInteraction - border.color: StudioTheme.Values.themeControlBackgroundInteraction - border.width: 2 - - Layout.preferredWidth: rowIdView.width - Layout.preferredHeight: headerView.height - Layout.minimumWidth: rowIdView.width - Layout.minimumHeight: headerView.height - - Text { - anchors.fill: parent - font: headerTextMetrics.font - text: "#" - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - color: StudioTheme.Values.themeTextColor - } - } - - HorizontalHeaderView { - id: headerView - - property real topPadding: 5 - property real bottomPadding: 5 - - Layout.preferredHeight: headerTextMetrics.height + topPadding + bottomPadding - Layout.columnSpan: 2 - syncView: tableView - clip: true - - delegate: HeaderDelegate { - selectedItem: tableView.model.selectedColumn - color: StudioTheme.Values.themeControlBackgroundInteraction - - MouseArea { - id: topHeaderMouseArea - - anchors.fill: parent - anchors.leftMargin: StudioTheme.Values.borderHover - anchors.rightMargin: StudioTheme.Values.borderHover - acceptedButtons: Qt.LeftButton | Qt.RightButton - hoverEnabled: true - onClicked: (mouse) => { - tableView.model.selectColumn(index) - - if (mouse.button === Qt.RightButton) { - let posX = index === root.model.columnCount() - 1 ? parent.width - editPropertyDialog.width : 0 - - headerMenu.clickedHeaderIndex = index - headerMenu.dialogPos = parent.mapToGlobal(posX, parent.height) - headerMenu.popup() - } else { - headerMenu.close() - } - } - } - - ToolTip { - id: topHeaderToolTip - - property bool expectedToBeShown: topHeaderMouseArea.containsMouse - visible: expectedToBeShown && text !== "" - delay: 1000 - - onExpectedToBeShownChanged: { - if (expectedToBeShown) - text = root.model.propertyType(index) - } - } - } - - StudioControls.Menu { - id: headerMenu - - property int clickedHeaderIndex: -1 - property point dialogPos - - onClosed: { - headerMenu.clickedHeaderIndex = -1 - } - - StudioControls.MenuItem { - text: qsTr("Edit") - onTriggered: editPropertyDialog.openDialog(headerMenu.clickedHeaderIndex, - headerMenu.dialogPos) - } - - StudioControls.MenuItem { - text: qsTr("Delete") - onTriggered: deleteColumnDialog.popUp(headerMenu.clickedHeaderIndex) - } - - StudioControls.MenuItem { - text: qsTr("Sort Ascending") - onTriggered: { - tableView.closeEditor() - tableView.model.sort(headerMenu.clickedHeaderIndex, Qt.AscendingOrder) - } - } - - StudioControls.MenuItem { - text: qsTr("Sort Descending") - onTriggered: { - tableView.closeEditor() - tableView.model.sort(headerMenu.clickedHeaderIndex, Qt.DescendingOrder) - } - } - } - } - - VerticalHeaderView { - id: rowIdView - - syncView: tableView - clip: true - - Layout.preferredHeight: tableView.height - Layout.rowSpan: 2 - Layout.alignment: Qt.AlignTop + Qt.AlignLeft - width: implicitWidth // suppresses GridLayout warnings when resizing - - delegate: HeaderDelegate { - selectedItem: tableView.model.selectedRow - color: StudioTheme.Values.themeControlBackgroundHover - - MouseArea { - anchors.fill: parent - anchors.topMargin: StudioTheme.Values.borderHover - anchors.bottomMargin: StudioTheme.Values.borderHover - acceptedButtons: Qt.LeftButton - onClicked: tableView.model.selectRow(index) - } - } - } - - TableView { - id: tableView - - model: root.sortedModel - clip: true - - readonly property real maxAvailableHeight: gridLayout.maxAvailableHeight - - addRowButton.height - - headerView.height - - (2 * gridLayout.rowSpacing) - readonly property real maxAvailableWidth: gridLayout.width - - StudioTheme.Values.collectionTableHorizontalMargin - - rowIdView.width - - addColumnButton.width - - gridLayout.columnSpacing - - property real childrenWidth: tableView.contentItem.childrenRect.width - property real childrenHeight: tableView.contentItem.childrenRect.height - - property int targetRow - property int targetColumn - - Layout.alignment: Qt.AlignTop + Qt.AlignLeft - Layout.preferredWidth: tableView.contentWidth - Layout.preferredHeight: tableView.contentHeight - Layout.minimumWidth: 100 - Layout.minimumHeight: 20 - Layout.maximumWidth: maxAvailableWidth - Layout.maximumHeight: maxAvailableHeight - - columnWidthProvider: function(column) { - if (!isColumnLoaded(column)) - return -1 - let w = explicitColumnWidth(column) - if (w < 0) - w = implicitColumnWidth(column) - return Math.max(w, StudioTheme.Values.collectionCellMinimumWidth) - } - - rowHeightProvider: function(row) { - if (!isRowLoaded(row)) - return -1 - let h = explicitRowHeight(row) - if (h < 0) - h = implicitRowHeight(row) - return Math.max(h, StudioTheme.Values.collectionCellMinimumHeight) - } - - function ensureRowIsVisible(row) { - let rows = tableView.model.rowCount() - let rowIsLoaded = tableView.isRowLoaded(row) - - if (row < 0 || row >= rows || rowIsLoaded) { - if (rowIsLoaded) - tableView.positionViewAtRow(row, Qt.AlignLeft | Qt.AlignTop) - - tableView.targetRow = -1 - return - } - - tableView.targetRow = row - tableView.positionViewAtRow(row, Qt.AlignLeft | Qt.AlignTop) - ensureTimer.start() - } - - function ensureColumnIsVisible(column) { - let columns = tableView.model.columnCount() - let columnIsLoaded = tableView.isColumnLoaded(column) - - if (column < 0 || column >= columns || columnIsLoaded) { - if (columnIsLoaded) - tableView.positionViewAtColumn(column, Qt.AlignLeft | Qt.AlignTop) - - tableView.targetColumn = -1 - return - } - - tableView.targetColumn = column - tableView.positionViewAtColumn(column, Qt.AlignLeft | Qt.AlignTop) - ensureTimer.start() - } - - onMaxAvailableHeightChanged: resetSizeTimer.start() - onMaxAvailableWidthChanged: resetSizeTimer.start() - onChildrenWidthChanged: resetSizeTimer.start() - onChildrenHeightChanged: resetSizeTimer.start() - - delegate: Rectangle { - id: itemCell - - clip: true - implicitWidth: 100 - implicitHeight: StudioTheme.Values.baseHeight - color: itemSelected ? StudioTheme.Values.themeControlBackgroundInteraction - : StudioTheme.Values.themeControlBackground - border.width: 1 - border.color: { - if (dataTypeWarning !== CollectionDetails.Warning.None) - return StudioTheme.Values.themeWarning - - if (itemSelected) - return StudioTheme.Values.themeControlOutlineInteraction - - return StudioTheme.Values.themeControlBackgroundInteraction - } - - HelperWidgets.ToolTipArea { - anchors.fill: parent - text: root.model.warningToString(dataTypeWarning) - enabled: dataTypeWarning !== CollectionDetails.Warning.None && text !== "" - hoverEnabled: true - acceptedButtons: Qt.NoButton - } - - MouseArea { - anchors.fill: parent - acceptedButtons: Qt.RightButton - onClicked: (mouse) => { - let row = index % tableView.model.rowCount() - - tableView.model.selectRow(row) - cellContextMenu.showMenu(row) - } - } - - Loader { - id: cellContentLoader - - property int cellColumnType: columnType ? columnType : 0 - - Component { - id: cellText - - Text { - text: display ?? "" - color: itemSelected ? StudioTheme.Values.themeInteraction - : StudioTheme.Values.themeTextColor - leftPadding: 5 - topPadding: 3 - bottomPadding: 3 - font.pixelSize: StudioTheme.Values.baseFontSize - horizontalAlignment: Text.AlignLeft - verticalAlignment: Text.AlignVCenter - elide: Text.ElideRight - } - } - - Component { - id: colorEditorComponent - - ColorViewDelegate {} - } - - function resetSource() { - if (columnType === CollectionDetails.DataType.Color) - cellContentLoader.sourceComponent = colorEditorComponent - else - cellContentLoader.sourceComponent = cellText - } - - Component.onCompleted: resetSource() - onCellColumnTypeChanged: resetSource() - } - - TableView.editDelegate: CollectionDetailsEditDelegate { - anchors { - top: itemCell.top - left: itemCell.left - } - Component.onCompleted: tableView.model.deselectAll() - } - } - - Timer { - id: resetSizeTimer - - interval: 100 - repeat: false - onTriggered: { - let cWidth = Math.min(tableView.maxAvailableWidth, tableView.childrenWidth) - let cHeight = Math.min(tableView.maxAvailableHeight, tableView.childrenHeight) - - if (tableView.contentWidth !== cWidth || tableView.contentHeight !== cHeight) - tableView.returnToBounds() - } - } - - Timer { - id: ensureTimer - - interval: 100 - repeat: false - onTriggered: { - tableView.ensureRowIsVisible(tableView.targetRow) - tableView.ensureColumnIsVisible(tableView.targetColumn) - } - } - - Connections { - target: tableView.model - - function onModelReset() { - root.closeDialogs() - tableView.clearColumnWidths() - tableView.clearRowHeights() - } - - function onRowsInserted(parent, first, last) { - tableView.closeEditor() - tableView.model.selectRow(first) - tableView.ensureRowIsVisible(first) - } - - function onColumnsInserted(parent, first, last) { - tableView.closeEditor() - tableView.model.selectColumn(first) - tableView.ensureColumnIsVisible(first) - } - - function onRowsRemoved(parent, first, last) { - let nextRow = first - 1 - if (nextRow < 0 && tableView.model.rowCount(parent) > 0) - nextRow = 0 - - tableView.model.selectRow(nextRow) - } - - function onColumnsRemoved(parent, first, last) { - let nextColumn = first - 1 - if (nextColumn < 0 && tableView.model.columnCount(parent) > 0) - nextColumn = 0 - - tableView.model.selectColumn(nextColumn) - } - } - - HoverHandler { id: hoverHandler } - - ScrollBar.horizontal: StudioControls.TransientScrollBar { - id: horizontalScrollBar - style: StudioTheme.Values.viewStyle - orientation: Qt.Horizontal - - show: (hoverHandler.hovered || tableView.focus || horizontalScrollBar.inUse) - && horizontalScrollBar.isNeeded - } - - ScrollBar.vertical: StudioControls.TransientScrollBar { - id: verticalScrollBar - style: StudioTheme.Values.viewStyle - orientation: Qt.Vertical - - show: (hoverHandler.hovered || tableView.focus || verticalScrollBar.inUse) - && verticalScrollBar.isNeeded - } - } - - HelperWidgets.IconButton { - id: addColumnButton - - iconSize:16 - Layout.preferredWidth: 24 - Layout.preferredHeight: tableView.height - Layout.minimumHeight: 24 - Layout.alignment: Qt.AlignLeft + Qt.AlignVCenter - - icon: StudioTheme.Constants.create_medium - tooltip: "Add Column" - - onClicked: toolbar.addNewColumn() - } - - HelperWidgets.IconButton { - id: addRowButton - - iconSize:16 - Layout.preferredWidth: tableView.width - Layout.preferredHeight: 24 - Layout.minimumWidth: 24 - Layout.alignment: Qt.AlignTop + Qt.AlignHCenter - - icon: StudioTheme.Constants.create_medium - tooltip: "Add Row" - - onClicked: toolbar.addNewRow() - } - - Item { - Layout.fillWidth: true - Layout.fillHeight: true - } - } - } - - ColumnLayout { - id: importsProblem - - visible: !topRow.visible && rootView.dataStoreExists && !rootView.projectImportExists - width: parent.width - anchors.verticalCenter: parent.verticalCenter - clip: true - - Text { - text: qsTr("Import the project to your design document to make the Model Editor enabled.") - Layout.alignment: Qt.AlignCenter - Layout.maximumWidth: parent.width - leftPadding: StudioTheme.Values.collectionItemTextPadding - rightPadding: StudioTheme.Values.collectionItemTextPadding - color: StudioTheme.Values.themeTextColor - font.pixelSize: StudioTheme.Values.mediumFontSize - wrapMode: Text.Wrap - } - - HelperWidgets.Button { - text: qsTr("Enable DataStore (This will add the required import)") - Layout.alignment: Qt.AlignCenter - onClicked: rootView.addProjectImport() - leftPadding: StudioTheme.Values.collectionItemTextPadding - rightPadding: StudioTheme.Values.collectionItemTextPadding - } - } - - Text { - anchors.centerIn: parent - text: qsTr("There are no models in this project.\nAdd or import a model.") - visible: !topRow.visible && !importsProblem.visible - color: StudioTheme.Values.themeTextColor - font.pixelSize: StudioTheme.Values.mediumFontSize - } - - TextMetrics { - id: headerTextMetrics - - font.pixelSize: StudioTheme.Values.baseFontSize - text: "Xq" - } - - StudioControls.Menu { - id: cellContextMenu - - property int rowIndex: -1 - - closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside - - function showMenu(rowIndex) { - cellContextMenu.rowIndex = rowIndex - cellContextMenu.popup() - } - - 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) - } - } - - component HeaderDelegate: Rectangle { - id: headerItem - - required property int selectedItem - property alias horizontalAlignment: headerText.horizontalAlignment - property alias verticalAlignment: headerText.verticalAlignment - - implicitWidth: headerText.implicitWidth - implicitHeight: headerText.implicitHeight - border.width: 1 - clip: true - - Text { - id: headerText - - topPadding: headerView.topPadding - bottomPadding: headerView.bottomPadding - leftPadding: 5 - rightPadding: 5 - text: display - font: headerTextMetrics.font - color: StudioTheme.Values.themeTextColor - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - elide: Text.ElideRight - } - - states: [ - State { - name: "default" - when: index !== selectedItem - PropertyChanges { - target: headerItem - border.color: StudioTheme.Values.themeControlBackgroundInteraction - } - - PropertyChanges { - target: headerText - font.bold: false - } - }, - State { - name: "selected" - when: index === selectedItem - - PropertyChanges { - target: headerItem - border.color: StudioTheme.Values.themeControlBackground - } - - PropertyChanges { - target: headerText - font.bold: true - } - } - ] - } - - component CellContextMenuItem: StudioControls.MenuItem { - id: cellContextMenuItemComponent - - property alias itemText: cellContextMenuText.text - property alias itemIcon: cellContextMenuIcon.text - text: "" - - implicitWidth: cellContextMenuRow.width - implicitHeight: cellContextMenuRow.height - - Row { - id: cellContextMenuRow - - property color textColor : cellContextMenuItemComponent.enabled - ? cellContextMenuItemComponent.highlighted - ? cellContextMenuItemComponent.style.text.selectedText - : cellContextMenuItemComponent.style.text.idle - : cellContextMenuItemComponent.style.text.disabled - - spacing: 2 * StudioTheme.Values.contextMenuHorizontalPadding - height: StudioTheme.Values.defaultControlHeight - leftPadding: StudioTheme.Values.contextMenuHorizontalPadding - rightPadding: StudioTheme.Values.contextMenuHorizontalPadding - - Text { - id: cellContextMenuIcon - - color: cellContextMenuRow.textColor - text: StudioTheme.Constants.addrowabove_medium - font.family: StudioTheme.Constants.iconFont.family - font.pixelSize: StudioTheme.Values.myIconFontSize - anchors.verticalCenter: parent.verticalCenter - } - - Text { - id: cellContextMenuText - - color: cellContextMenuRow.textColor - anchors.verticalCenter: parent.verticalCenter - } - } - } - - EditPropertyDialog { - id: editPropertyDialog - model: root.model - } - - StudioControls.Dialog { - id: deleteColumnDialog - - property int clickedIndex: -1 - - title: qsTr("Delete Column") - width: 400 - - onAccepted: { - root.model.removeColumn(clickedIndex) - } - - function popUp(index) - { - deleteColumnDialog.clickedIndex = index - deleteColumnDialog.open() - } - - contentItem: ColumnLayout { - spacing: StudioTheme.Values.sectionColumnSpacing - - Text { - text: qsTr("Are you sure that you want to delete column \"%1\"?").arg( - root.model.headerData( - deleteColumnDialog.clickedIndex, Qt.Horizontal)) - color: StudioTheme.Values.themeTextColor - } - - RowLayout { - Layout.alignment: Qt.AlignRight | Qt.AlignVCenter - spacing: StudioTheme.Values.sectionRowSpacing - - HelperWidgets.Button { - text: qsTr("Delete") - onClicked: deleteColumnDialog.accept() - } - - HelperWidgets.Button { - text: qsTr("Cancel") - onClicked: deleteColumnDialog.reject() - } - } - } - } -} diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionItem.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionItem.qml deleted file mode 100644 index d963070536f..00000000000 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionItem.qml +++ /dev/null @@ -1,154 +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 -import CollectionEditorBackend - -Item { - id: root - - implicitWidth: 300 - implicitHeight: boundingRect.height + 3 - - property color textColor - readonly property string name: collectionName ?? "" - readonly property bool isSelected: collectionIsSelected - readonly property int id: index - - function rename(newName) { - collectionName = newName - } - - signal selectItem(int itemIndex) - signal deleteItem() - signal contextMenuRequested() - - Item { - id: boundingRect - - width: parent.width - height: itemLayout.height - clip: true - - MouseArea { - id: itemMouse - - anchors.fill: parent - acceptedButtons: Qt.LeftButton - propagateComposedEvents: true - hoverEnabled: true - onClicked: (event) => { - if (!collectionIsSelected) { - collectionIsSelected = true - event.accepted = true - } - } - } - - Rectangle { - id: innerRect - anchors.fill: parent - } - - RowLayout { - id: itemLayout - - width: parent.width - - Text { - id: nameHolder - - Layout.fillWidth: true - Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter - Layout.leftMargin: StudioTheme.Values.collectionItemTextSideMargin - Layout.topMargin: StudioTheme.Values.collectionItemTextMargin - Layout.bottomMargin: StudioTheme.Values.collectionItemTextMargin - - text: collectionName - font.pixelSize: StudioTheme.Values.baseFontSize - color: root.textColor - topPadding: StudioTheme.Values.collectionItemTextPadding - bottomPadding: StudioTheme.Values.collectionItemTextPadding - elide: Text.ElideMiddle - verticalAlignment: Text.AlignVCenter - } - - Text { - id: threeDots - - Layout.alignment: Qt.AlignRight | Qt.AlignVCenter - Layout.topMargin: StudioTheme.Values.collectionItemTextMargin - Layout.bottomMargin: StudioTheme.Values.collectionItemTextMargin - Layout.rightMargin: StudioTheme.Values.collectionItemTextSideMargin - - text: StudioTheme.Constants.more_medium - font.family: StudioTheme.Constants.iconFont.family - font.pixelSize: StudioTheme.Values.baseIconFontSize - color: root.textColor - padding: StudioTheme.Values.collectionItemTextPadding - - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - - MouseArea { - anchors.fill: parent - acceptedButtons: Qt.RightButton | Qt.LeftButton - onClicked: contextMenuRequested() - } - } - } - } - - states: [ - State { - name: "default" - when: !collectionIsSelected && !itemMouse.containsMouse - - PropertyChanges { - target: innerRect - opacity: 0.6 - color: StudioTheme.Values.themeControlBackground - } - - PropertyChanges { - target: root - textColor: StudioTheme.Values.themeTextColor - } - }, - State { - name: "hovered" - when: !collectionIsSelected && itemMouse.containsMouse - - PropertyChanges { - target: innerRect - opacity: 0.8 - color: StudioTheme.Values.themeControlBackgroundHover - } - - PropertyChanges { - target: root - textColor: StudioTheme.Values.themeTextColor - } - }, - State { - name: "selected" - when: collectionIsSelected - - PropertyChanges { - target: innerRect - opacity: 1 - color: StudioTheme.Values.themeIconColorSelected - } - - PropertyChanges { - target: root - textColor: StudioTheme.Values.themeTextSelectedTextColor - } - } - ] -} diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionListView.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionListView.qml deleted file mode 100644 index 2b95abfc4f4..00000000000 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionListView.qml +++ /dev/null @@ -1,215 +0,0 @@ -// 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 QtQuick.Layouts -import HelperWidgets 2.0 as HelperWidgets -import StudioControls 1.0 as StudioControls -import StudioTheme as StudioTheme -import CollectionEditorBackend - -ListView { - id: root - - model: CollectionEditorBackend.model - clip: true - - function closeDialogs() { - currentCollection.dereference() - collectionMenu.close() - deleteDialog.reject() - renameDialog.reject() - } - - delegate: CollectionItem { - implicitWidth: root.width - onDeleteItem: root.model.removeRow(index) - onContextMenuRequested: collectionMenu.openMenu(this) - } - - QtObject { - id: currentCollection - - property CollectionItem item - readonly property string name: item ? item.name : "" - readonly property bool selected: item ? item.isSelected : false - readonly property int index: item ? item.id : -1 - - function rename(newName) { - if (item) - item.rename(newName) - } - - function deleteItem() { - if (item) - item.deleteItem() - } - - function dereference() { - item = null - } - } - - StudioControls.Menu { - id: collectionMenu - - closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside - - function openMenu(item) { - currentCollection.item = item - popup() - } - - StudioControls.MenuItem { - text: qsTr("Delete") - shortcut: StandardKey.Delete - onTriggered: deleteDialog.open() - } - - StudioControls.MenuItem { - text: qsTr("Rename") - shortcut: StandardKey.Replace - onTriggered: renameDialog.open() - } - - StudioControls.MenuItem { - text: qsTr("Assign to the selected node") - enabled: CollectionEditorBackend.rootView.targetNodeSelected - onTriggered: rootView.assignCollectionToSelectedNode(currentCollection.name) - } - } - - StudioControls.Dialog { - id: deleteDialog - - title: qsTr("Deleting the model") - clip: true - - onAccepted: currentCollection.deleteItem() - - contentItem: ColumnLayout { - id: deleteDialogContent // Keep the id here even if it's not used, because the dialog might lose implicitSize - - width: 300 - spacing: 2 - - Text { - Layout.fillWidth: true - - wrapMode: Text.WordWrap - color: StudioTheme.Values.themeTextColor - text: qsTr("Are you sure that you want to delete model \"%1\"?" - + "\nThe model will be deleted permanently.").arg(currentCollection.name) - - } - - Spacer {} - - RowLayout { - spacing: StudioTheme.Values.sectionRowSpacing - Layout.alignment: Qt.AlignRight | Qt.AlignVCenter - Layout.fillWidth: true - Layout.preferredHeight: 40 - - HelperWidgets.Button { - text: qsTr("Delete") - onClicked: deleteDialog.accept() - } - - HelperWidgets.Button { - text: qsTr("Cancel") - onClicked: deleteDialog.reject() - } - } - } - } - - StudioControls.Dialog { - id: renameDialog - - title: qsTr("Rename model") - - onAccepted: { - if (newNameField.text !== "") - currentCollection.rename(newNameField.text) - } - - onOpened: { - newNameField.text = currentCollection.name - } - - contentItem: ColumnLayout { - spacing: 2 - - Text { - text: qsTr("Previous name: " + currentCollection.name) - color: StudioTheme.Values.themeTextColor - } - - Spacer {} - - Text { - Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter - text: qsTr("New name:") - color: StudioTheme.Values.themeTextColor - } - - StudioControls.TextField { - id: newNameField - - Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter - Layout.fillWidth: true - - actionIndicator.visible: false - translationIndicator.visible: false - validator: newNameValidator - - Keys.onEnterPressed: renameDialog.accept() - Keys.onReturnPressed: renameDialog.accept() - Keys.onEscapePressed: renameDialog.reject() - - onTextChanged: { - btnRename.enabled = newNameField.text !== "" - } - } - - Spacer {} - - RowLayout { - Layout.alignment: Qt.AlignRight | Qt.AlignVCenter - spacing: StudioTheme.Values.sectionRowSpacing - - HelperWidgets.Button { - id: btnRename - - text: qsTr("Rename") - onClicked: renameDialog.accept() - } - - HelperWidgets.Button { - text: qsTr("Cancel") - onClicked: renameDialog.reject() - } - } - } - } - - Connections { - target: root.model - - function onModelReset() { - root.closeDialogs() - } - } - - RegularExpressionValidator { - id: newNameValidator - regularExpression: /^\w+$/ - } - - component Spacer: Item { - implicitWidth: 1 - implicitHeight: StudioTheme.Values.columnGap - } -} diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml deleted file mode 100644 index 9d483037ac8..00000000000 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml +++ /dev/null @@ -1,206 +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 as HelperWidgets -import StudioTheme as StudioTheme -import CollectionEditorBackend - -Item { - id: root - focus: true - - property var rootView: CollectionEditorBackend.rootView - property var model: CollectionEditorBackend.model - property var collectionDetailsModel: CollectionEditorBackend.collectionDetailsModel - property var collectionDetailsSortFilterModel: CollectionEditorBackend.collectionDetailsSortFilterModel - - function showWarning(title, message) { - warningDialog.title = title - warningDialog.message = message - warningDialog.open() - } - - // called from C++ when using the delete key - function deleteSelectedCollection() { - print("TODO: deleteSelectedCollection") - } - - function closeDialogs() { - importDialog.reject() - newCollection.reject() - warningDialog.reject() - } - - ImportDialog { - id: importDialog - - backendValue: root.rootView - anchors.centerIn: parent - } - - NewCollectionDialog { - id: newCollection - - anchors.centerIn: parent - } - - Message { - id: warningDialog - - title: "" - message: "" - } - - Rectangle { - // Covers the toolbar color on top to prevent the background - // color for the margin of splitter - - 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 - - onOrientationChanged: detailsView.closeDialogs() - - 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 - - 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 - anchors.left: parent.left - anchors.leftMargin: StudioTheme.Values.toolbarHorizontalMargin - - text: qsTr("Data Models") - font.pixelSize: StudioTheme.Values.baseFontSize - color: StudioTheme.Values.themeTextColor - } - - HelperWidgets.AbstractButton { - id: importCollectionButton - - anchors.verticalCenter: parent.verticalCenter - anchors.right: parent.right - anchors.rightMargin: StudioTheme.Values.toolbarHorizontalMargin - - style: StudioTheme.Values.viewBarButtonStyle - buttonIcon: StudioTheme.Constants.import_medium - tooltip: qsTr("Import a model") - - onClicked: importDialog.open() - } - } - - CollectionListView { // Model Groups - id: collectionListView - - Layout.fillWidth: true - Layout.minimumHeight: bottomSpacer.isExpanded ? 150 : 0 - Layout.fillHeight: !bottomSpacer.isExpanded - Layout.preferredHeight: contentHeight - Layout.maximumHeight: contentHeight - Layout.alignment: Qt.AlignTop | Qt.AlignHCenter - } - - HelperWidgets.IconButton { - id: addCollectionButton - - iconSize: 16 - Layout.fillWidth: true - Layout.minimumWidth: 24 - Layout.alignment: Qt.AlignTop | Qt.AlignHCenter - - tooltip: qsTr("Add a new model") - icon: StudioTheme.Constants.create_medium - onClicked: newCollection.open() - } - - Item { - id: bottomSpacer - - readonly property bool isExpanded: height > 0 - Layout.minimumWidth: 1 - Layout.fillHeight: true - } - } - - CollectionDetailsView { - id: detailsView - - model: root.collectionDetailsModel - backend: root.model - sortedModel: root.collectionDetailsSortFilterModel - SplitView.fillHeight: true - SplitView.fillWidth: true - } - } - - Connections { - target: root.model - - function onModelReset() { - root.closeDialogs() - } - } -} diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/ColorViewDelegate.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/ColorViewDelegate.qml deleted file mode 100644 index 16e55acfb43..00000000000 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/ColorViewDelegate.qml +++ /dev/null @@ -1,296 +0,0 @@ -// 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.Layouts -import QtQuick.Shapes -import QtQuick.Templates as T -import HelperWidgets 2.0 as HelperWidgets -import StudioTheme as StudioTheme -import StudioControls as StudioControls -import QtQuickDesignerTheme -import QtQuickDesignerColorPalette - -Row { - id: colorEditor - - property color color - property bool supportGradient: false - - property QtObject backendValue: QtObject { - property color value: edit - readonly property color editColor: edit - - function resetValue() { - if (value) - value = "" - } - - onValueChanged: { - if (editColor !== value) - edit = value - } - } - - property variant value: { - if (!colorEditor.backendValue || !colorEditor.backendValue.value) - return "white" // default color for Rectangle - - if (colorEditor.isVector3D) { - return Qt.rgba(colorEditor.backendValue.value.x, - colorEditor.backendValue.value.y, - colorEditor.backendValue.value.z, 1) - } - - return colorEditor.backendValue.value - } - - property alias gradientPropertyName: popupDialog.gradientPropertyName - - property alias gradientThumbnail: gradientThumbnail - property alias shapeGradientThumbnail: shapeGradientThumbnail - - property bool shapeGradients: false - property bool isVector3D: false - property color originalColor - - property bool __block: false - - function resetShapeColor() { - colorEditor.backendValue.resetValue() - } - - function writeColor() { - if (colorEditor.isVector3D) { - colorEditor.backendValue.value = Qt.vector3d(colorEditor.color.r, - colorEditor.color.g, - colorEditor.color.b) - } else { - colorEditor.backendValue.value = colorEditor.color - } - } - - function initEditor() { - colorEditor.syncColor() - } - - // Syncing color from backend to frontend and block reflection - function syncColor() { - colorEditor.__block = true - colorEditor.color = colorEditor.value - hexTextField.syncColor() - colorEditor.__block = false - } - - Connections { - id: backendConnection - - target: colorEditor - - function onValueChanged() { - if (popupDialog.isSolid()) - colorEditor.syncColor() - } - - function onBackendValueChanged() { - if (popupDialog.isSolid()) - colorEditor.syncColor() - } - } - - Timer { - id: colorEditorTimer - - repeat: false - interval: 100 - running: false - onTriggered: { - backendConnection.enabled = false - colorEditor.writeColor() - hexTextField.syncColor() - backendConnection.enabled = true - } - } - - onColorChanged: { - if (colorEditor.__block) - return - - if (!popupDialog.isInValidState) - return - - popupDialog.commitToGradient() - - // Delay setting the color to keep ui responsive - if (popupDialog.isSolid()) - colorEditorTimer.restart() - } - - Rectangle { - id: preview - - implicitWidth: StudioTheme.Values.twoControlColumnWidth - implicitHeight: StudioTheme.Values.height - color: colorEditor.color - border.color: StudioTheme.Values.themeControlOutline - border.width: StudioTheme.Values.border - - Rectangle { - id: gradientThumbnail - - anchors.fill: parent - anchors.margins: StudioTheme.Values.border - visible: !popupDialog.isSolid() - && !colorEditor.shapeGradients - && popupDialog.isLinearGradient() - } - - Shape { - id: shape - - anchors.fill: parent - anchors.margins: StudioTheme.Values.border - visible: !popupDialog.isSolid() && colorEditor.shapeGradients - - ShapePath { - id: shapeGradientThumbnail - - startX: shape.x - 1 - startY: shape.y - 1 - strokeWidth: -1 - strokeColor: "green" - - PathLine { - x: shape.x - 1 - y: shape.height - } - PathLine { - x: shape.width - y: shape.height - } - PathLine { - x: shape.width - y: shape.y - 1 - } - } - } - - Image { - anchors.fill: parent - source: "qrc:/navigator/icon/checkers.png" - fillMode: Image.Tile - z: -1 - } - - MouseArea { - anchors.fill: parent - onClicked: { - popupDialog.visibility ? popupDialog.close() : popupDialog.open() - forceActiveFocus() - } - } - - StudioControls.PopupDialog { - id: popupDialog - - property bool isInValidState: loader.active ? popupDialog.loaderItem.isInValidState : true - property QtObject loaderItem: loader.item - property string gradientPropertyName - - keepOpen: loader.item?.eyeDropperActive ?? false - - width: 260 - - function commitToGradient() { - if (!loader.active) - return - - if (colorEditor.supportGradient && popupDialog.loaderItem.gradientModel.hasGradient) { - var hexColor = convertColorToString(colorEditor.color) - hexTextField.text = hexColor - colorEditor.backendValue.value = hexColor - popupDialog.loaderItem.commitGradientColor() - } - } - - function isSolid() { - if (!loader.active) - return true - - return popupDialog.loaderItem.isSolid() - } - - function isLinearGradient(){ - if (!loader.active) - return false - - return popupDialog.loaderItem.isLinearGradient() - } - - function ensureLoader() { - if (!loader.active) - loader.active = true - } - - function open() { - popupDialog.ensureLoader() - popupDialog.loaderItem.initEditor() - popupDialog.show(preview) - } - - function determineActiveColorMode() { - if (loader.active && popupDialog.loaderItem) - popupDialog.loaderItem.determineActiveColorMode() - else - colorEditor.syncColor() - } - - Loader { - id: loader - - active: colorEditor.supportGradient - - sourceComponent: HelperWidgets.ColorEditorPopup { - shapeGradients: colorEditor.shapeGradients - supportGradient: colorEditor.supportGradient - width: popupDialog.contentWidth - } - - onLoaded: { - popupDialog.loaderItem.initEditor() - popupDialog.titleBar = loader.item.titleBarContent - } - } - } - } - - HelperWidgets.LineEdit { - id: hexTextField - implicitWidth: StudioTheme.Values.twoControlColumnWidth - + StudioTheme.Values.actionIndicatorWidth - width: hexTextField.implicitWidth - enabled: popupDialog.isSolid() - writeValueManually: true - validator: RegularExpressionValidator { - regularExpression: /#[0-9A-Fa-f]{6}([0-9A-Fa-f]{2})?/g - } - showTranslateCheckBox: false - showExtendedFunctionButton: false - indicatorVisible: false - - onAccepted: colorEditor.color = hexTextField.text - onCommitData: { - colorEditor.color = hexTextField.text - if (popupDialog.isSolid()) - colorEditor.writeColor() - } - - function syncColor() { - hexTextField.text = colorEditor.color - } - } - - Component.onCompleted: popupDialog.determineActiveColorMode() - - onBackendValueChanged: popupDialog.determineActiveColorMode() -} diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/EditPropertyDialog.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/EditPropertyDialog.qml deleted file mode 100644 index 546dc5d770c..00000000000 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/EditPropertyDialog.qml +++ /dev/null @@ -1,158 +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.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 - - required property var model - property int __propertyIndex: -1 - property string __currentName - - title: qsTr("Edit Column") - - function openDialog(index, initialPosition) { - root.__propertyIndex = index - - if (root.__propertyIndex < 0) - return - - root.__currentName = root.model.propertyName(root.__propertyIndex) - nameTextField.text = root.__currentName - nameTextField.selectAll() - nameTextField.forceActiveFocus() - - typeComboBox.initialType = root.model.propertyType(root.__propertyIndex) - typeComboBox.currentIndex = typeComboBox.find(typeComboBox.initialType) - - let newPoint = mapFromGlobal(initialPosition.x, initialPosition.y) - x = newPoint.x - y = newPoint.y - - root.open() - } - - onWidthChanged: { - if (visible && x > parent.width) - root.close() - } - - onAccepted: { - if (nameTextField.text !== "" && nameTextField.text !== root.__currentName) - root.model.renameColumn(root.__propertyIndex, nameTextField.text) - - if (typeComboBox.initialType !== typeComboBox.currentText) - root.model.setPropertyType(root.__propertyIndex, typeComboBox.currentText) - } - - contentItem: Column { - spacing: 5 - - Grid { - columns: 2 - rows: 2 - rowSpacing: 2 - columnSpacing: 25 - verticalItemAlignment: Grid.AlignVCenter - - Text { - text: qsTr("Name") - color: StudioTheme.Values.themeTextColor - verticalAlignment: Text.AlignVCenter - } - - StudioControls.TextField { - id: nameTextField - - actionIndicator.visible: false - translationIndicator.visible: false - - Keys.onEnterPressed: root.accept() - Keys.onReturnPressed: root.accept() - Keys.onEscapePressed: root.reject() - - validator: RegularExpressionValidator { - regularExpression: /^\w+$/ - } - } - - Text { - text: qsTr("Type") - color: StudioTheme.Values.themeTextColor - } - - StudioControls.ComboBox { - id: typeComboBox - - property string initialType - - model: CollectionDataTypeModel{} - textRole: "display" - tooltipRole: "toolTip" - actionIndicatorVisible: false - } - } - - Rectangle { - id: warningBox - - visible: typeComboBox.initialType !== typeComboBox.currentText - color: "transparent" - clip: true - border.color: StudioTheme.Values.themeWarning - width: parent.width - height: warning.height - - Row { - id: warning - - padding: 5 - spacing: 5 - - HelperWidgets.IconLabel { - icon: StudioTheme.Constants.warning_medium - anchors.verticalCenter: parent.verticalCenter - } - - Text { - text: qsTr("Conversion from %1 to %2 may lead to data loss") - .arg(typeComboBox.initialType) - .arg(typeComboBox.currentText) - - width: warningBox.width - 20 - - color: StudioTheme.Values.themeTextColor - wrapMode: Text.WordWrap - } - } - } - - Row { - height: 40 - spacing: 5 - - HelperWidgets.Button { - id: editButton - - text: qsTr("Apply") - enabled: nameTextField.text !== "" - anchors.bottom: parent.bottom - - onClicked: root.accept() - } - - HelperWidgets.Button { - text: qsTr("Cancel") - anchors.bottom: parent.bottom - - onClicked: root.reject() - } - } - } -} diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/IconTextButton.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/IconTextButton.qml deleted file mode 100644 index fe9d094349c..00000000000 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/IconTextButton.qml +++ /dev/null @@ -1,97 +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 StudioTheme as StudioTheme - -Rectangle { - id: root - - required property string text - required property string icon - - property alias tooltip: toolTip.text - property StudioTheme.ControlStyle style: StudioTheme.Values.viewBarButtonStyle - property int fontSize: StudioTheme.Values.baseFontSize - - implicitHeight: style.squareControlSize.height - implicitWidth: rowAlign.width - - signal clicked() - - RowLayout { - id: rowAlign - - anchors.verticalCenter: parent.verticalCenter - spacing: StudioTheme.Values.inputHorizontalPadding - - Text { - id: iconItem - - Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter - text: root.icon - color: StudioTheme.Values.themeTextColor - font.family: StudioTheme.Constants.iconFont.family - font.pixelSize: StudioTheme.Values.bigFont - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - leftPadding: StudioTheme.Values.inputHorizontalPadding - } - - Text { - id: textItem - - Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter - text: root.text - color: StudioTheme.Values.themeTextColor - font.family: StudioTheme.Constants.font.family - font.pixelSize: StudioTheme.Values.baseFontSize - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - rightPadding: StudioTheme.Values.inputHorizontalPadding - } - } - - MouseArea { - id: mouseArea - anchors.fill: parent - hoverEnabled: true - onClicked: root.clicked() - } - - ToolTip { - id: toolTip - - visible: mouseArea.containsMouse && text !== "" - delay: 1000 - } - - states: [ - State { - name: "default" - when: !mouseArea.pressed && !mouseArea.containsMouse - PropertyChanges { - target: root - color: StudioTheme.Values.themeControlBackground - } - }, - State { - name: "Pressed" - when: mouseArea.pressed - PropertyChanges { - target: root - color: StudioTheme.Values.themeControlBackgroundInteraction - } - }, - State { - name: "Hovered" - when: !mouseArea.pressed && mouseArea.containsMouse - PropertyChanges { - target: root - color: StudioTheme.Values.themeControlBackgroundHover - } - } - ] -} diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/ImportDialog.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/ImportDialog.qml deleted file mode 100644 index 21a5e4e8e04..00000000000 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/ImportDialog.qml +++ /dev/null @@ -1,225 +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 Qt.labs.platform as PlatformWidgets -import HelperWidgets as HelperWidgets -import StudioControls as StudioControls -import StudioTheme as StudioTheme - -StudioControls.Dialog { - id: root - - title: qsTr("Import a model") - anchors.centerIn: parent - closePolicy: Popup.CloseOnEscape - modal: true - - required property var backendValue - - property bool fileExists: false - - onOpened: { - collectionName.text = "Model" - fileName.text = qsTr("Model path") - fileName.selectAll() - fileName.forceActiveFocus() - } - - onRejected: { - fileName.text = "" - } - - function acceptIfIsValid() { - if (btnImport.enabled) - btnImport.onClicked() - } - - RegularExpressionValidator { - id: fileNameValidator - regularExpression: /^(\w[^*> { + confirmDeleteDialog.targetBundleItem = bundleItem + confirmDeleteDialog.targetBundleLabel = "material" + confirmDeleteDialog.open() + } + onCountChanged: root.responsiveResize(stackLayout.width, stackLayout.height) } } diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryEffectContextMenu.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryEffectContextMenu.qml index 1e0bcf1eb40..008f99d4fa7 100644 --- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryEffectContextMenu.qml +++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryEffectContextMenu.qml @@ -12,7 +12,7 @@ StudioControls.Menu { property var targetItem: null - readonly property bool targetAvailable: targetItem && !ContentLibraryBackend.effectsModel.importerRunning + readonly property bool targetAvailable: targetItem && !ContentLibraryBackend.rootView.importerRunning signal unimport(var bundleEff); diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryEffectsView.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryEffectsView.qml index f6df99156bf..199f784db18 100644 --- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryEffectsView.qml +++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryEffectsView.qml @@ -91,7 +91,7 @@ HelperWidgets.ScrollView { id: repeater model: bundleCategoryItems - delegate: ContentLibraryEffect { + delegate: ContentLibraryItem { width: root.cellWidth height: root.cellHeight diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryEffect.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryItem.qml similarity index 90% rename from share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryEffect.qml rename to share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryItem.qml index b20cc71e156..e6e5cd05c30 100644 --- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryEffect.qml +++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryItem.qml @@ -23,8 +23,8 @@ Item { acceptedButtons: Qt.LeftButton | Qt.RightButton onPressed: (mouse) => { - if (mouse.button === Qt.LeftButton && !ContentLibraryBackend.effectsModel.importerRunning) - ContentLibraryBackend.rootView.startDragEffect(modelData, mapToGlobal(mouse.x, mouse.y)) + if (mouse.button === Qt.LeftButton && !ContentLibraryBackend.rootView.importerRunning) + ContentLibraryBackend.rootView.startDragItem(modelData, mapToGlobal(mouse.x, mouse.y)) else if (mouse.button === Qt.RightButton) root.showContextMenu() } @@ -43,7 +43,7 @@ Item { source: modelData.bundleItemIcon cache: false - Rectangle { // circular indicator for imported bundle effect + Rectangle { // circular indicator for imported bundle item width: 10 height: 10 radius: 5 @@ -57,7 +57,7 @@ Item { ToolTip { visible: indicatorMouseArea.containsMouse - text: qsTr("Effect is imported to project") + text: qsTr("Item is imported to the project") delay: 1000 } diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryItemContextMenu.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryItemContextMenu.qml new file mode 100644 index 00000000000..6f505124300 --- /dev/null +++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryItemContextMenu.qml @@ -0,0 +1,71 @@ +// 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 as StudioControls +import StudioTheme as StudioTheme +import ContentLibraryBackend + +StudioControls.Menu { + id: root + + property var targetItem: null + property bool enableRemove: false // true: adds an option to remove targetItem + + readonly property bool targetAvailable: targetItem && !ContentLibraryBackend.rootView.importerRunning + + signal unimport(); + signal addToProject() + signal applyToSelected(bool add) + signal removeFromContentLib() + + function popupMenu(item = null) + { + root.targetItem = item + + let isMaterial = root.targetItem.itemType === "material" + applyToSelectedReplace.visible = isMaterial + applyToSelectedAdd.visible = isMaterial + + popup() + } + + closePolicy: StudioControls.Menu.CloseOnEscape | StudioControls.Menu.CloseOnPressOutside + + StudioControls.MenuItem { + id: applyToSelectedReplace + text: qsTr("Apply to selected (replace)") + height: visible ? implicitHeight : 0 + enabled: root.targetAvailable && ContentLibraryBackend.rootView.hasModelSelection + onTriggered: root.applyToSelected(false) + } + + StudioControls.MenuItem { + id: applyToSelectedAdd + text: qsTr("Apply to selected (add)") + height: visible ? implicitHeight : 0 + enabled: root.targetAvailable && ContentLibraryBackend.rootView.hasModelSelection + onTriggered: root.applyToSelected(true) + } + + StudioControls.MenuSeparator {} + + StudioControls.MenuItem { + enabled: root.targetAvailable + text: qsTr("Add an instance to project") + onTriggered: root.addToProject() + } + + StudioControls.MenuItem { + enabled: root.targetAvailable && root.targetItem.bundleItemImported + text: qsTr("Remove from project") + onTriggered: root.unimport() + } + + StudioControls.MenuItem { + text: qsTr("Remove from Content Library") + visible: root.enableRemove && root.targetAvailable + height: visible ? implicitHeight : 0 + onTriggered: root.removeFromContentLib() + } +} diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterial.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterial.qml index 0e9fc4903eb..20ba17d8041 100644 --- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterial.qml +++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterial.qml @@ -16,8 +16,6 @@ Item { // "failed" property string downloadState: modelData.isDownloaded() ? "downloaded" : "" - property bool importerRunning: false - signal showContextMenu() signal addToProject() @@ -32,7 +30,7 @@ Item { acceptedButtons: Qt.LeftButton | Qt.RightButton onPressed: (mouse) => { - if (mouse.button === Qt.LeftButton && !root.importerRunning) { + if (mouse.button === Qt.LeftButton && !ContentLibraryBackend.rootView.importerRunning) { if (root.downloadState === "downloaded") ContentLibraryBackend.rootView.startDragMaterial(modelData, mapToGlobal(mouse.x, mouse.y)) } else if (mouse.button === Qt.RightButton && root.downloadState === "downloaded") { @@ -74,7 +72,7 @@ Item { color: "#00ff00" border.color: "#555555" border.width: 1 - visible: modelData.bundleMaterialImported + visible: modelData.bundleItemImported ToolTip { visible: indicatorMouseArea.containsMouse @@ -99,7 +97,7 @@ Item { pressColor: Qt.hsla(c.hslHue, c.hslSaturation, c.hslLightness, .4) anchors.right: img.right anchors.bottom: img.bottom - enabled: !root.importerRunning + enabled: !ContentLibraryBackend.rootView.importerRunning visible: root.downloadState === "downloaded" && (containsMouse || mouseArea.containsMouse) @@ -186,7 +184,7 @@ Item { baseUrl: modelData.bundleMaterialBaseWebUrl files: modelData.bundleMaterialFiles - targetDirPath: modelData.bundleMaterialParentPath + targetDirPath: modelData.bundleMaterialDirPath onDownloadStarting: { root.downloadState = "downloading" diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterialContextMenu.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterialContextMenu.qml deleted file mode 100644 index b67ec311ef0..00000000000 --- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterialContextMenu.qml +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -import QtQuick 2.15 -import HelperWidgets 2.0 -import StudioControls 1.0 as StudioControls -import StudioTheme 1.0 as StudioTheme - -StudioControls.Menu { - id: root - - property var targetMaterial: null - property bool hasModelSelection: false - property bool importerRunning: false - - readonly property bool targetAvailable: targetMaterial && !importerRunning - - signal unimport(); - signal addToProject() - signal applyToSelected(bool add) - - function popupMenu(targetMaterial = null) - { - this.targetMaterial = targetMaterial - popup() - } - - closePolicy: StudioControls.Menu.CloseOnEscape | StudioControls.Menu.CloseOnPressOutside - - StudioControls.MenuItem { - text: qsTr("Apply to selected (replace)") - enabled: root.targetAvailable && root.hasModelSelection - onTriggered: root.applyToSelected(false) - } - - StudioControls.MenuItem { - text: qsTr("Apply to selected (add)") - enabled: root.targetAvailable && root.hasModelSelection - onTriggered: root.applyToSelected(true) - } - - StudioControls.MenuSeparator {} - - StudioControls.MenuItem { - enabled: root.targetAvailable - text: qsTr("Add an instance to project") - - onTriggered: { - root.addToProject() - } - } - - StudioControls.MenuItem { - enabled: root.targetAvailable && root.targetMaterial.bundleMaterialImported - text: qsTr("Remove from project") - - onTriggered: root.unimport() - } -} diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterialsView.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterialsView.qml index 9a0e33b8e5e..e9fca319736 100644 --- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterialsView.qml +++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryMaterialsView.qml @@ -46,16 +46,13 @@ HelperWidgets.ScrollView { } Column { - ContentLibraryMaterialContextMenu { + ContentLibraryItemContextMenu { id: ctxMenu - hasModelSelection: root.materialsModel.hasModelSelection - importerRunning: root.materialsModel.importerRunning + onApplyToSelected: (add) => root.materialsModel.applyToSelected(ctxMenu.targetItem, add) - onApplyToSelected: (add) => root.materialsModel.applyToSelected(ctxMenu.targetMaterial, add) - - onUnimport: root.unimport(ctxMenu.targetMaterial) - onAddToProject: root.materialsModel.addToProject(ctxMenu.targetMaterial) + onUnimport: root.unimport(ctxMenu.targetItem) + onAddToProject: root.materialsModel.addToProject(ctxMenu.targetItem) } Repeater { @@ -103,8 +100,6 @@ HelperWidgets.ScrollView { width: root.cellWidth height: root.cellHeight - importerRunning: root.materialsModel.importerRunning - onShowContextMenu: ctxMenu.popupMenu(modelData) onAddToProject: root.materialsModel.addToProject(modelData) } diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryTextureContextMenu.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryTextureContextMenu.qml index f804f16d893..b1f690f10c9 100644 --- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryTextureContextMenu.qml +++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryTextureContextMenu.qml @@ -12,6 +12,7 @@ StudioControls.Menu { property var targetTexture: null property bool hasSceneEnv: false + property bool enableRemove: false // true: adds an option to remove targetTexture property bool canUse3D: targetTexture && ContentLibraryBackend.rootView.hasQuick3DImport && ContentLibraryBackend.rootView.hasMaterialLibrary @@ -32,13 +33,20 @@ StudioControls.Menu { StudioControls.MenuItem { text: qsTr("Add texture") - enabled: canUse3D + enabled: root.canUse3D onTriggered: ContentLibraryBackend.rootView.addTexture(root.targetTexture) } StudioControls.MenuItem { text: qsTr("Add light probe") - enabled: root.hasSceneEnv && canUse3D + enabled: root.hasSceneEnv && root.canUse3D onTriggered: ContentLibraryBackend.rootView.addLightProbe(root.targetTexture) } + + StudioControls.MenuItem { + text: qsTr("Remove from Content Library") + visible: root.targetTexture && root.enableRemove + height: visible ? implicitHeight : 0 + onTriggered: ContentLibraryBackend.userModel.removeTexture(root.targetTexture) + } } diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryUserView.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryUserView.qml index 1288e14c2a7..cced9e5f095 100644 --- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryUserView.qml +++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/ContentLibraryUserView.qml @@ -12,7 +12,7 @@ HelperWidgets.ScrollView { id: root clip: true - interactive: !ctxMenuMaterial.opened && !ctxMenuTexture.opened + interactive: !ctxMenuItem.opened && !ctxMenuTexture.opened && !ContentLibraryBackend.rootView.isDragging && !HelperWidgets.Controller.contextMenuOpened property real cellWidth: 100 @@ -31,9 +31,10 @@ HelperWidgets.ScrollView { required property var searchBox signal unimport(var bundleItem); + signal removeFromContentLib(var bundleItem); function closeContextMenu() { - ctxMenuMaterial.close() + ctxMenuItem.close() ctxMenuTexture.close() } @@ -46,21 +47,22 @@ HelperWidgets.ScrollView { } Column { - ContentLibraryMaterialContextMenu { - id: ctxMenuMaterial + ContentLibraryItemContextMenu { + id: ctxMenuItem - hasModelSelection: ContentLibraryBackend.userModel.hasModelSelection - importerRunning: ContentLibraryBackend.userModel.importerRunning + enableRemove: true - onApplyToSelected: (add) => ContentLibraryBackend.userModel.applyToSelected(ctxMenuMaterial.targetMaterial, add) + onApplyToSelected: (add) => ContentLibraryBackend.userModel.applyToSelected(ctxMenuItem.targetItem, add) - onUnimport: root.unimport(ctxMenuMaterial.targetMaterial) - onAddToProject: ContentLibraryBackend.userModel.addToProject(ctxMenuMaterial.targetMaterial) + onUnimport: root.unimport(ctxMenuItem.targetItem) + onAddToProject: ContentLibraryBackend.userModel.addToProject(ctxMenuItem.targetItem) + onRemoveFromContentLib: root.removeFromContentLib(ctxMenuItem.targetItem) } ContentLibraryTextureContextMenu { id: ctxMenuTexture + enableRemove: true hasSceneEnv: ContentLibraryBackend.texturesModel.hasSceneEnv } @@ -79,7 +81,7 @@ HelperWidgets.ScrollView { bottomPadding: StudioTheme.Values.sectionPadding caption: categoryName - visible: categoryVisible + visible: categoryVisible && infoText.text === "" category: "ContentLib_User" function expandSection() { @@ -90,8 +92,6 @@ HelperWidgets.ScrollView { onCountChanged: root.assignMaxCount() - property int numVisibleItem: 1 // initially, the tab is invisible so this will be 0 - Grid { width: section.width - section.leftPadding - section.rightPadding spacing: StudioTheme.Values.sectionGridSpacing @@ -110,14 +110,8 @@ HelperWidgets.ScrollView { width: root.cellWidth height: root.cellHeight - importerRunning: ContentLibraryBackend.userModel.importerRunning - - onShowContextMenu: ctxMenuMaterial.popupMenu(modelData) + onShowContextMenu: ctxMenuItem.popupMenu(modelData) onAddToProject: ContentLibraryBackend.userModel.addToProject(modelData) - - onVisibleChanged: { - section.numVisibleItem += visible ? 1 : -1 - } } } DelegateChoice { @@ -129,6 +123,15 @@ HelperWidgets.ScrollView { onShowContextMenu: ctxMenuTexture.popupMenu(modelData) } } + DelegateChoice { + roleValue: "item" + delegate: ContentLibraryItem { + width: root.cellWidth + height: root.cellHeight + + onShowContextMenu: ctxMenuItem.popupMenu(modelData) + } + } } onCountChanged: root.assignMaxCount() @@ -140,7 +143,7 @@ HelperWidgets.ScrollView { color: StudioTheme.Values.themeTextColor font.pixelSize: StudioTheme.Values.baseFontSize leftPadding: 10 - visible: !searchBox.isEmpty() && section.numVisibleItem === 0 + visible: infoText.text === "" && !searchBox.isEmpty() && categoryNoMatch } } } @@ -148,9 +151,7 @@ HelperWidgets.ScrollView { Text { id: infoText text: { - if (!ContentLibraryBackend.effectsModel.bundleExists) - qsTr("User bundle couldn't be found.") - else if (!ContentLibraryBackend.rootView.isQt6Project) + if (!ContentLibraryBackend.rootView.isQt6Project) qsTr("Content Library is not supported in Qt5 projects.") else if (!ContentLibraryBackend.rootView.hasQuick3DImport) qsTr("To use Content Library, first add the QtQuick3D module in the Components view.") @@ -163,7 +164,7 @@ HelperWidgets.ScrollView { font.pixelSize: StudioTheme.Values.baseFontSize topPadding: 10 leftPadding: 10 - visible: ContentLibraryBackend.effectsModel.isEmpty + visible: infoText.text !== "" } } } diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/DeleteBundleItemDialog.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/DeleteBundleItemDialog.qml new file mode 100644 index 00000000000..d8f6551ae59 --- /dev/null +++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/DeleteBundleItemDialog.qml @@ -0,0 +1,65 @@ +// 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 QtQuick.Layouts +import HelperWidgets +import StudioControls as StudioControls +import StudioTheme as StudioTheme +import ContentLibraryBackend + +StudioControls.Dialog { + id: root + + property var targetBundleItem + property var targetBundleModel + property string targetBundleLabel // "effect" or "material" + + title: qsTr("Remove bundle %1").arg(root.targetBundleLabel) + anchors.centerIn: parent + closePolicy: Popup.CloseOnEscape + implicitWidth: 300 + modal: true + + onOpened: warningText.forceActiveFocus() + + contentItem: Column { + spacing: 20 + width: parent.width + + Text { + id: warningText + + text: qsTr("Are you sure you? The action cannot be undone") + color: StudioTheme.Values.themeTextColor + wrapMode: Text.WordWrap + anchors.right: parent.right + anchors.left: parent.left + leftPadding: 10 + rightPadding: 10 + + Keys.onEnterPressed: btnRemove.onClicked() + Keys.onReturnPressed: btnRemove.onClicked() + } + + Row { + anchors.right: parent.right + Button { + id: btnRemove + + text: qsTr("Remove") + + onClicked: { + ContentLibraryBackend.userModel.removeFromContentLib(root.targetBundleItem) + root.accept() + } + } + + Button { + text: qsTr("Cancel") + onClicked: root.reject() + } + } + } +} diff --git a/share/qtcreator/qmldesigner/contentLibraryQmlSource/UnimportBundleMaterialDialog.qml b/share/qtcreator/qmldesigner/contentLibraryQmlSource/UnimportBundleItemDialog.qml similarity index 96% rename from share/qtcreator/qmldesigner/contentLibraryQmlSource/UnimportBundleMaterialDialog.qml rename to share/qtcreator/qmldesigner/contentLibraryQmlSource/UnimportBundleItemDialog.qml index 4385e3bf82e..305e1018e37 100644 --- a/share/qtcreator/qmldesigner/contentLibraryQmlSource/UnimportBundleMaterialDialog.qml +++ b/share/qtcreator/qmldesigner/contentLibraryQmlSource/UnimportBundleItemDialog.qml @@ -13,8 +13,8 @@ StudioControls.Dialog { id: root property var targetBundleItem - property var targetBundleLabel // "effect" or "material" property var targetBundleModel + property string targetBundleLabel // "effect" or "material" title: qsTr("Bundle %1 might be in use").arg(root.targetBundleLabel) anchors.centerIn: parent diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerTopBar.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerTopBar.qml index 8f98ae25b25..799dbeeddc3 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerTopBar.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/EffectComposerTopBar.qml @@ -89,8 +89,8 @@ Rectangle { 2. Adjust the effect nodes properties 3. Change the order of the effects, if you like 4. See the preview -5. Save in the library, if you wish to reuse the effect later") // TODO: revise with doc engineer +5. Save in the assets library, if you wish to reuse the effect later") - onClicked: Qt.openUrlExternally("https://doc.qt.io/qtdesignstudio/qt-using-effect-maker-effects.html") + onClicked: Qt.openUrlExternally("https://doc.qt.io/qtdesignstudio/qtquick-effect-composer-view.html") } } diff --git a/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueFloat.qml b/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueFloat.qml index 969d7e29492..6b723a9bc59 100644 --- a/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueFloat.qml +++ b/share/qtcreator/qmldesigner/effectComposerQmlSources/ValueFloat.qml @@ -42,8 +42,9 @@ Row { to: uniformMaxValue value: uniformValue onMoved: { - uniformValue = value - spinBox.value = value // binding isn't working for this property so update it + let fixedValue = Number.parseFloat(value).toFixed(slider.decimals) + uniformValue = fixedValue + spinBox.value = fixedValue // binding isn't working for this property so update it } } } diff --git a/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialBrowserContextMenu.qml b/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialBrowserContextMenu.qml index b01aa7d2a3c..248e07bf6c4 100644 --- a/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialBrowserContextMenu.qml +++ b/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialBrowserContextMenu.qml @@ -128,12 +128,10 @@ StudioControls.Menu { onTriggered: materialBrowserModel.addNewMaterial() } - Component.onCompleted: { - if (MaterialBrowserBackend.rootView.userBundleEnabled()) { - var menuItem = Qt.createQmlObject("import StudioControls as StudioControls; StudioControls.MenuItem {}", root) - menuItem.text = qsTr("Add to Content Library") - menuItem.onTriggered.connect(MaterialBrowserBackend.rootView.addMaterialToContentLibrary) - root.addItem(menuItem) - } + StudioControls.MenuItem { + text: qsTr("Add to Content Library") + enabled: !materialBrowserModel.selectedMaterialIsComponent + + onTriggered: MaterialBrowserBackend.rootView.addMaterialToContentLibrary() } } diff --git a/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorPane.qml b/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorPane.qml index dde7ecd0dc2..0ab7e59d01f 100644 --- a/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorPane.qml +++ b/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorPane.qml @@ -4,8 +4,8 @@ import QtQuick import HelperWidgets -PropertyEditorPane { - id: itemPane +Item { + id: root width: 420 height: 420 @@ -17,63 +17,75 @@ PropertyEditorPane { // invoked from C++ to refresh material preview image function refreshPreview() { - topSection.refreshPreview() + itemPane.headerItem.refreshPreview() } // Called from C++ to close context menu on focus out function closeContextMenu() { - topSection.closeContextMenu() + itemPane.headerItem.closeContextMenu() Controller.closeContextMenu() } // Called from C++ to initialize preview menu checkmarks function initPreviewData(env, model) { - topSection.previewEnv = env; - topSection.previewModel = model + itemPane.headerItem.previewEnv = env + itemPane.headerItem.previewModel = model } - MaterialEditorTopSection { - id: topSection + MaterialEditorToolBar { + id: toolbar - onToolBarAction: (action) => itemPane.toolBarAction(action) - onPreviewEnvChanged: itemPane.previewEnvChanged(previewEnv) - onPreviewModelChanged: itemPane.previewModelChanged(previewModel) + width: parent.width + + onToolBarAction: (action) => root.toolBarAction(action) } - Item { width: 1; height: 10 } + PropertyEditorPane { + id: itemPane - DynamicPropertiesSection { - propertiesModel: MaterialEditorDynamicPropertiesModel {} - } + anchors.top: toolbar.bottom + anchors.bottom: parent.bottom + width: parent.width - Loader { - id: specificsTwo + clip: true - property string theSource: specificQmlData + headerComponent: MaterialEditorTopSection { + onPreviewEnvChanged: root.previewEnvChanged(previewEnv) + onPreviewModelChanged: root.previewModelChanged(previewModel) + } - anchors.left: parent.left - anchors.right: parent.right - visible: specificsTwo.theSource !== "" - sourceComponent: specificQmlComponent + DynamicPropertiesSection { + propertiesModel: MaterialEditorDynamicPropertiesModel {} + } - onTheSourceChanged: { - specificsTwo.active = false - specificsTwo.active = true + Loader { + id: specificsTwo + + property string theSource: specificQmlData + + width: parent.width + visible: specificsTwo.theSource !== "" + sourceComponent: specificQmlComponent + + onTheSourceChanged: { + specificsTwo.active = false + specificsTwo.active = true + } + } + + Item { // spacer + width: 1 + height: 10 + visible: specificsTwo.visible + } + + Loader { + id: specificsOne + anchors.left: parent.left + anchors.right: parent.right + source: specificsUrl } } - - Item { - width: 1 - height: 10 - visible: specificsTwo.visible - } - - Loader { - id: specificsOne - anchors.left: parent.left - anchors.right: parent.right - source: specificsUrl - } } diff --git a/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorTopSection.qml b/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorTopSection.qml index 23311636bc2..d60c98933b6 100644 --- a/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorTopSection.qml +++ b/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorTopSection.qml @@ -11,10 +11,11 @@ import StudioTheme as StudioTheme Column { id: root - signal toolBarAction(int action) - property string previewEnv property string previewModel + + property real __horizontalSpacing: 5 + property StudioTheme.ControlStyle buttonStyle: StudioTheme.ViewBarButtonStyle { //This is how you can override stuff from the control styles controlSize: Qt.size(previewOptions.width, previewOptions.width) @@ -37,14 +38,7 @@ Column { anchors.left: parent.left anchors.right: parent.right - MaterialEditorToolBar { - width: root.width - - onToolBarAction: (action) => root.toolBarAction(action) - } - - Item { width: 1; height: 10 } // spacer - + Item { width: 1; height: 5 } // spacer StudioControls.Menu { id: modelMenu @@ -129,6 +123,19 @@ Column { width: parent.width height: previewRect.height + StudioControls.AbstractButton { + id: pinButton + + x: root.__horizontalSpacing + + style: root.buttonStyle + iconSize: StudioTheme.Values.bigFont + buttonIcon: pinButton.checked ? StudioTheme.Constants.pin : StudioTheme.Constants.unpin + checkable: true + checked: itemPane.headerDocked + onCheckedChanged: itemPane.headerDocked = pinButton.checked + } + Rectangle { id: previewRect anchors.horizontalCenter: parent.horizontalCenter @@ -153,6 +160,7 @@ Column { height: previewRect.height anchors.top: previewRect.top anchors.left: previewRect.right + anchors.leftMargin: root.__horizontalSpacing Column { anchors.horizontalCenter: parent.horizontalCenter @@ -172,7 +180,6 @@ Column { } } } - } HelperWidgets.Section { @@ -214,6 +221,7 @@ Column { model: possibleTypes showExtendedFunctionButton: false implicitWidth: StudioTheme.Values.singleControlColumnWidth + enabled: possibleTypes.length > 1 onActivated: changeTypeName(currentValue) } diff --git a/share/qtcreator/qmldesigner/projectstorage/fake.qmltypes b/share/qtcreator/qmldesigner/projectstorage/fake.qmltypes index f6f21596899..60096cd7900 100644 --- a/share/qtcreator/qmldesigner/projectstorage/fake.qmltypes +++ b/share/qtcreator/qmldesigner/projectstorage/fake.qmltypes @@ -318,4 +318,13 @@ Component { Component { name: "QScxmlError" } + +Component { + name: "QList" +} + +Component { + name: "VertexColorMaskFlags" +} + } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/BusyIndicatorSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/BusyIndicatorSpecifics.qml new file mode 100644 index 00000000000..2cc82d7fabd --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/BusyIndicatorSpecifics.qml @@ -0,0 +1,6 @@ +// 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 ".." as Original + +Original.BusyIndicatorSpecifics {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ButtonSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ButtonSpecifics.qml new file mode 100644 index 00000000000..ea32a005941 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ButtonSpecifics.qml @@ -0,0 +1,6 @@ +// 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 ".." as Original + +Original.ButtonSpecifics {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/CheckBoxSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/CheckBoxSpecifics.qml new file mode 100644 index 00000000000..c38b75b327b --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/CheckBoxSpecifics.qml @@ -0,0 +1,6 @@ +// 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 ".." as Original + +Original.CheckBoxSpecifics {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/CheckDelegateSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/CheckDelegateSpecifics.qml new file mode 100644 index 00000000000..939433f5eaf --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/CheckDelegateSpecifics.qml @@ -0,0 +1,6 @@ +// 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 ".." as Original + +Original.CheckDelegateSpecifics {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ComboBoxSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ComboBoxSpecifics.qml new file mode 100644 index 00000000000..13ad9346d40 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ComboBoxSpecifics.qml @@ -0,0 +1,6 @@ +// 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 ".." as Original + +Original.ComboBoxSpecifics {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ControlSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ControlSpecifics.qml new file mode 100644 index 00000000000..07cc704a071 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ControlSpecifics.qml @@ -0,0 +1,6 @@ +// 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 ".." as Original + +Original.ControlSpecifics {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/DelayButtonSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/DelayButtonSpecifics.qml new file mode 100644 index 00000000000..3818cebc33b --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/DelayButtonSpecifics.qml @@ -0,0 +1,6 @@ +// 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 ".." as Original + +Original.DelayButtonSpecifics {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/DialSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/DialSpecifics.qml new file mode 100644 index 00000000000..c62608cd004 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/DialSpecifics.qml @@ -0,0 +1,6 @@ +// 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 ".." as Original + +Original.DialSpecifics {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/DialogSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/DialogSpecifics.qml new file mode 100644 index 00000000000..237cb418622 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/DialogSpecifics.qml @@ -0,0 +1,6 @@ +// 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 ".." as Original + +Original.DialogSpecifics {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/DrawerSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/DrawerSpecifics.qml new file mode 100644 index 00000000000..d5cf0a482eb --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/DrawerSpecifics.qml @@ -0,0 +1,6 @@ +// 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 ".." as Original + +Original.DrawerSpecifics {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/FrameSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/FrameSpecifics.qml new file mode 100644 index 00000000000..ac1881a865d --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/FrameSpecifics.qml @@ -0,0 +1,6 @@ +// 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 ".." as Original + +Original.FrameSpecifics {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/GroupBoxSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/GroupBoxSpecifics.qml new file mode 100644 index 00000000000..3067af76405 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/GroupBoxSpecifics.qml @@ -0,0 +1,6 @@ +// 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 ".." as Original + +Original.GroupBoxSpecifics {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ItemDelegateSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ItemDelegateSpecifics.qml new file mode 100644 index 00000000000..6c7f5dc8127 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ItemDelegateSpecifics.qml @@ -0,0 +1,6 @@ +// 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 ".." as Original + +Original.ItemDelegateSpecifics {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/LabelSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/LabelSpecifics.qml new file mode 100644 index 00000000000..6c826e46ead --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/LabelSpecifics.qml @@ -0,0 +1,6 @@ +// 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 ".." as Original + +Original.LabelSpecifics {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/PageIndicatorSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/PageIndicatorSpecifics.qml new file mode 100644 index 00000000000..7437975f960 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/PageIndicatorSpecifics.qml @@ -0,0 +1,6 @@ +// 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 ".." as Original + +Original.PageIndicatorSpecifics {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/PageSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/PageSpecifics.qml new file mode 100644 index 00000000000..16e8b51cb52 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/PageSpecifics.qml @@ -0,0 +1,6 @@ +// 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 ".." as Original + +Original.PageSpecifics {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/PaneSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/PaneSpecifics.qml new file mode 100644 index 00000000000..dd07a3cf7fa --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/PaneSpecifics.qml @@ -0,0 +1,6 @@ +// 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 ".." as Original + +Original.PaneSpecifics {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/PopupSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/PopupSpecifics.qml new file mode 100644 index 00000000000..788b2a43593 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/PopupSpecifics.qml @@ -0,0 +1,6 @@ +// 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 ".." as Original + +Original.PopupSpecifics {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ProgressBarSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ProgressBarSpecifics.qml new file mode 100644 index 00000000000..a58f9e2f360 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ProgressBarSpecifics.qml @@ -0,0 +1,6 @@ +// 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 ".." as Original + +Original.ProgressBarSpecifics {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/RadioButtonSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/RadioButtonSpecifics.qml new file mode 100644 index 00000000000..fc2500dd1b7 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/RadioButtonSpecifics.qml @@ -0,0 +1,6 @@ +// 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 ".." as Original + +Original.RadioButtonSpecifics {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/RadioDelegateSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/RadioDelegateSpecifics.qml new file mode 100644 index 00000000000..d07f17b7e85 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/RadioDelegateSpecifics.qml @@ -0,0 +1,6 @@ +// 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 ".." as Original + +Original.RadioDelegateSpecifics {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/RangeSliderSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/RangeSliderSpecifics.qml new file mode 100644 index 00000000000..14e493e69e9 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/RangeSliderSpecifics.qml @@ -0,0 +1,6 @@ +// 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 ".." as Original + +Original.RangeSliderSpecifics {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/RoundButtonSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/RoundButtonSpecifics.qml new file mode 100644 index 00000000000..5bd8776f624 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/RoundButtonSpecifics.qml @@ -0,0 +1,6 @@ +// 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 ".." as Original + +Original.RoundButtonSpecifics {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ScrollViewSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ScrollViewSpecifics.qml new file mode 100644 index 00000000000..2af497fcb71 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ScrollViewSpecifics.qml @@ -0,0 +1,6 @@ +// 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 ".." as Original + +Original.ScrollViewSpecifics {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/SliderSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/SliderSpecifics.qml new file mode 100644 index 00000000000..846ca8bb14f --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/SliderSpecifics.qml @@ -0,0 +1,6 @@ +// 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 ".." as Original + +Original.SliderSpecifics {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/SpinBoxSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/SpinBoxSpecifics.qml new file mode 100644 index 00000000000..f75983a9fad --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/SpinBoxSpecifics.qml @@ -0,0 +1,6 @@ +// 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 ".." as Original + +Original.SpinBoxSpecifics {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/StackViewSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/StackViewSpecifics.qml new file mode 100644 index 00000000000..593cba24d75 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/StackViewSpecifics.qml @@ -0,0 +1,6 @@ +// 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 ".." as Original + +Original.StackViewSpecifics {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/SwipeDelegateSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/SwipeDelegateSpecifics.qml new file mode 100644 index 00000000000..40648a34313 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/SwipeDelegateSpecifics.qml @@ -0,0 +1,6 @@ +// 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 ".." as Original + +Original.SwipeDelegateSpecifics {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/SwipeViewSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/SwipeViewSpecifics.qml new file mode 100644 index 00000000000..eaf769135c1 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/SwipeViewSpecifics.qml @@ -0,0 +1,6 @@ +// 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 ".." as Original + +Original.SwipeViewSpecifics {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/SwitchDelegateSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/SwitchDelegateSpecifics.qml new file mode 100644 index 00000000000..cba96b930f1 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/SwitchDelegateSpecifics.qml @@ -0,0 +1,6 @@ +// 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 ".." as Original + +Original.SwitchDelegateSpecifics {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/SwitchSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/SwitchSpecifics.qml new file mode 100644 index 00000000000..e6647f4f7d4 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/SwitchSpecifics.qml @@ -0,0 +1,6 @@ +// 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 ".." as Original + +Original.SwitchSpecifics {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/TabBarSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/TabBarSpecifics.qml new file mode 100644 index 00000000000..c23580a25e9 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/TabBarSpecifics.qml @@ -0,0 +1,6 @@ +// 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 ".." as Original + +Original.TabBarSpecifics {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/TabButtonSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/TabButtonSpecifics.qml new file mode 100644 index 00000000000..dc5bcb36e2e --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/TabButtonSpecifics.qml @@ -0,0 +1,6 @@ +// 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 ".." as Original + +Original.TabButtonSpecifics {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/TextAreaSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/TextAreaSpecifics.qml new file mode 100644 index 00000000000..a9871612a97 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/TextAreaSpecifics.qml @@ -0,0 +1,6 @@ +// 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 ".." as Original + +Original.TextAreaSpecifics {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/TextFieldSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/TextFieldSpecifics.qml new file mode 100644 index 00000000000..fbcac9c3898 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/TextFieldSpecifics.qml @@ -0,0 +1,6 @@ +// 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 ".." as Original + +Original.TextFieldSpecifics {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ToolBarSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ToolBarSpecifics.qml new file mode 100644 index 00000000000..63d1f179eb0 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ToolBarSpecifics.qml @@ -0,0 +1,6 @@ +// 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 ".." as Original + +Original.ToolBarSpecifics {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ToolButtonSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ToolButtonSpecifics.qml new file mode 100644 index 00000000000..98aabdd2742 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ToolButtonSpecifics.qml @@ -0,0 +1,6 @@ +// 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 ".." as Original + +Original.ToolButtonSpecifics {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ToolSeparatorSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ToolSeparatorSpecifics.qml new file mode 100644 index 00000000000..060808a2a6d --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/ToolSeparatorSpecifics.qml @@ -0,0 +1,6 @@ +// 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 ".." as Original + +Original.ToolSeparatorSpecifics {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/TumblerSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/TumblerSpecifics.qml new file mode 100644 index 00000000000..5ca82806427 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/Controls/Basic/TumblerSpecifics.qml @@ -0,0 +1,6 @@ +// 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 ".." as Original + +Original.TumblerSpecifics {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/EffectsSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/EffectsSection.qml index 2b7543f1d99..3e9a79f8e04 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/EffectsSection.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/EffectsSection.qml @@ -33,7 +33,7 @@ Section { if (!root.hasDesignerEffect) return - root.model = modelNodeBackend.allChildren(effect[0]) //ids for all effects + root.model = modelNodeBackend.allChildren(effect[0]) // ids for all effects } leftPadding: 0 @@ -50,6 +50,8 @@ Section { } SectionLayout { + x: StudioTheme.Values.sectionLeftPadding + PropertyLabel {} SecondColumnLayout { @@ -61,7 +63,7 @@ Section { width: StudioTheme.Values.singleControlColumnWidth buttonIcon: root.hasDesignerEffect ? qsTr("Remove Effects") : qsTr("Add Effects") iconFont: StudioTheme.Constants.font - tooltip: qsTr("Adds a note with a title to explain the component.") + tooltip: qsTr("Adds visual effects on the component.") onClicked: { if (root.hasDesignerEffect) { root.effectNodeWrapper.deleteModelNode() @@ -77,6 +79,7 @@ Section { PropertyLabel { text: qsTr("Visible") + tooltip: qsTr("Toggles the visibility of visual effects on the component.") visible: root.hasDesignerEffect } @@ -192,7 +195,10 @@ Section { SectionLayout { - PropertyLabel { text: qsTr("Visible") } + PropertyLabel { + text: qsTr("Visible") + tooltip: qsTr("Toggles the visibility of the Layer Blur on the component.") + } SecondColumnLayout { CheckBox { @@ -204,7 +210,10 @@ Section { ExpandingSpacer {} } - PropertyLabel { text: qsTr("Blur") } + PropertyLabel { + text: qsTr("Blur") + tooltip: qsTr("Sets the intensity of the Layer Blur on the component.") + } SecondColumnLayout { SpinBox { @@ -232,7 +241,10 @@ Section { SectionLayout { - PropertyLabel { text: qsTr("Visible") } + PropertyLabel { + text: qsTr("Visible") + tooltip: qsTr("Toggles the visibility of blur on the selected background component.") + } SecondColumnLayout { CheckBox { @@ -244,7 +256,12 @@ Section { ExpandingSpacer {} } - PropertyLabel { text: qsTr("Blur") } + PropertyLabel { + text: qsTr("Blur") + tooltip: qsTr("Sets the intensity of blur on the selected background component.\n" + + "The foreground component should be transparent, and the background " + + "component should be opaque.") + } SecondColumnLayout { SpinBox { @@ -258,7 +275,12 @@ Section { ExpandingSpacer {} } - PropertyLabel { text: qsTr("Background") } + PropertyLabel { + text: qsTr("Background") + tooltip: qsTr("Sets a component as the background of a transparent component." + + "The Background Blur works only on this component. The component should " + + "be solid.") + } SecondColumnLayout { ItemFilterComboBox { @@ -362,7 +384,10 @@ Section { id: controlContainer property bool isDropShadow: shadowComboBox.currentValue === "DesignDropShadow" - PropertyLabel { text: qsTr("Visible") } + PropertyLabel { + text: qsTr("Visible") + tooltip: qsTr("Toggles the visibility of the component shadow.") + } SecondColumnLayout { CheckBox { @@ -374,7 +399,11 @@ Section { ExpandingSpacer {} } - PropertyLabel { text: qsTr("Blur") } + PropertyLabel { + text: qsTr("Blur") + tooltip: qsTr("Sets the softness of the component shadow. A larger value" + + " causes the edges of the shadow to appear more blurry.") + } SecondColumnLayout { SpinBox { @@ -390,6 +419,9 @@ Section { PropertyLabel { text: qsTr("Spread") + tooltip: modelNodeBackend.isInstanceOf("Rectangle") + ? qsTr("Resizes the base shadow of the component by pixels.") + : qsTr("Only supported for Rectangles.") enabled: modelNodeBackend.isInstanceOf("Rectangle") } @@ -408,7 +440,7 @@ Section { PropertyLabel { text: qsTr("Color") - tooltip: qsTr("Sets the color.") + tooltip: qsTr("Sets the color of the shadow.") } ColorEditor { @@ -416,7 +448,11 @@ Section { supportGradient: false } - PropertyLabel { text: qsTr("Offset") } + PropertyLabel { + text: qsTr("Offset") + tooltip: qsTr("Moves the shadow with respect to the component in " + + "X and Y coordinates by pixels.") + } SecondColumnLayout { SpinBox { @@ -458,6 +494,7 @@ Section { PropertyLabel { visible: controlContainer.isDropShadow text: qsTr("Show behind") + tooltip: qsTr("Toggles the visibility of the shadow behind a transparent component.") } SecondColumnLayout { @@ -483,6 +520,7 @@ Section { } SectionLayout { + x: StudioTheme.Values.sectionLeftPadding visible: root.hasDesignerEffect PropertyLabel {} @@ -496,7 +534,7 @@ Section { width: StudioTheme.Values.singleControlColumnWidth buttonIcon: qsTr("Add Shadow Effect") iconFont: StudioTheme.Constants.font - tooltip: qsTr("Adds a Design Drop Shadow.") + tooltip: qsTr("Adds Drop Shadow or Inner Shadow effects to a component.") onClicked: { modelNodeBackend.createModelNode(root.effectNode, "effects", @@ -504,6 +542,8 @@ Section { root.invalidate() } } + + ExpandingSpacer {} } } } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditor.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditor.qml index 4b4d2b8dc6a..cc8247d7063 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditor.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditor.qml @@ -38,6 +38,10 @@ SecondColumnLayout { property alias showHexTextField: hexTextField.visible property bool shapeGradients: false + + //for now, gradients on MCUs are limited to Basic and Shape Linear Gradient: + property bool mcuGradients: false + property color originalColor property bool isVector3D: false @@ -219,7 +223,10 @@ SecondColumnLayout { function open() { popupDialog.ensureLoader() + popupDialog.show(preview) + + popupDialog.loaderItem.aboutToBeShown() //need it for now } function determineActiveColorMode() { @@ -235,9 +242,11 @@ SecondColumnLayout { sourceComponent: ColorEditorPopup { shapeGradients: colorEditor.shapeGradients + mcuGradients: colorEditor.mcuGradients supportGradient: colorEditor.supportGradient width: popupDialog.contentWidth visible: popupDialog.visible + parentWindow: popupDialog.window } onLoaded: { diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditorPopup.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditorPopup.qml index 22be367c378..41f2c433fa9 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditorPopup.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditorPopup.qml @@ -12,10 +12,20 @@ import QtQuickDesignerColorPalette Column { id: root + // There seems to be an issue on Windows and MacOS with ColorPickers + // Canvases not being painted on initialization + // because ColorEditorPopup is invisible at init time, + // so we use this signal to explicitly pass visibility status + signal aboutToBeShown + property bool eyeDropperActive: ColorPaletteBackend.eyeDropperActive property bool supportGradient: false property bool shapeGradients: false + + //for now, gradients on MCUs are limited to Basic and Shape Linear Gradient: + property bool mcuGradients: false + property alias gradientLine: gradientLine property alias popupHexTextField: popupHexTextField property alias gradientPropertyName: root.gradientModel.gradientPropertyName @@ -23,6 +33,8 @@ Column { property alias gradientModel: gradientModel + property Window parentWindow: null + property bool isInValidState: false readonly property real twoColumnWidth: (colorColumn.width - StudioTheme.Values.controlGap) * 0.5 @@ -224,12 +236,12 @@ Column { ceMode.items.append({ value: "RadialGradient", text: qsTr("Radial"), - enabled: root.supportGradient && root.shapeGradients + enabled: root.supportGradient && root.shapeGradients && !root.mcuGradients }) ceMode.items.append({ value: "ConicalGradient", text: qsTr("Conical"), - enabled: root.supportGradient && root.shapeGradients + enabled: root.supportGradient && root.shapeGradients && !root.mcuGradients }) } @@ -429,6 +441,13 @@ Column { hsvValueSpinBox.value = colorPicker.value hsvAlphaSpinBox.value = colorPicker.alpha } + + Connections { + target: root + onAboutToBeShown: { + colorPicker.aboutToBeShown() + } + } } Column { diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/DynamicPropertiesSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/DynamicPropertiesSection.qml index 88a0debae80..e0c70ff946b 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/DynamicPropertiesSection.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/DynamicPropertiesSection.qml @@ -528,6 +528,8 @@ Section { return intEditor if (propertyType == "real") return realEditor + if (propertyType == "double") + return realEditor if (propertyType == "string") return stringEditor if (propertyType == "bool") @@ -708,7 +710,7 @@ Section { StudioControls.ComboBox { id: comboBox actionIndicator.visible: false - model: ["int", "real", "color", "string", "bool", "url", "alias", "signal", + model: ["int", "real", "double", "color", "string", "bool", "url", "alias", "signal", "TextureInput", "vector2d", "vector3d", "vector4d"] width: cePopup.itemWidth } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ExtendedFunctionLogic.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ExtendedFunctionLogic.qml index 76e6bd8f094..8a8c8c33ce6 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ExtendedFunctionLogic.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ExtendedFunctionLogic.qml @@ -103,7 +103,7 @@ Item { StudioControls.MenuItem { text: qsTr("Insert Keyframe") enabled: hasActiveTimeline - onTriggered: insertKeyframe(backendValue.name) + onTriggered: backendValue.insertKeyframe() } } } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/PropertyEditorPane.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/PropertyEditorPane.qml index 7046ca48e14..309d815d7aa 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/PropertyEditorPane.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/PropertyEditorPane.qml @@ -19,30 +19,83 @@ Rectangle { default property alias content: mainColumn.children property alias scrollView: mainScrollView + property bool headerDocked: false + readonly property Item headerItem: headerDocked ? dockedHeaderLoader.item : undockedHeaderLoader.item + + property Component headerComponent: null + // Called from C++ to close context menu on focus out function closeContextMenu() { Controller.closeContextMenu() } + Loader { + id: dockedHeaderLoader + + anchors.top: itemPane.top + z: parent.z + 1 + height: item ? item.implicitHeight : 0 + width: parent.width + + active: itemPane.headerDocked + sourceComponent: itemPane.headerComponent + + HeaderBackground{} + } + MouseArea { anchors.fill: parent - onClicked: forceActiveFocus() + onClicked: itemPane.forceActiveFocus() } HelperWidgets.ScrollView { id: mainScrollView - //clip: true - anchors.fill: parent + + clip: true + anchors { + top: dockedHeaderLoader.bottom + bottom: itemPane.bottom + left: itemPane.left + right: itemPane.right + } interactive: !Controller.contextMenuOpened - Column { - id: mainColumn - y: -1 - width: itemPane.width + ColumnLayout { + spacing: 2 + width: mainScrollView.width - onWidthChanged: StudioTheme.Values.responsiveResize(itemPane.width) - Component.onCompleted: StudioTheme.Values.responsiveResize(itemPane.width) + Loader { + id: undockedHeaderLoader + + Layout.fillWidth: true + Layout.preferredHeight: item ? item.implicitHeight : 0 + + active: !itemPane.headerDocked + sourceComponent: itemPane.headerComponent + + HeaderBackground{} + } + + Column { + id: mainColumn + + Layout.fillWidth: true + + onWidthChanged: StudioTheme.Values.responsiveResize(itemPane.width) + Component.onCompleted: StudioTheme.Values.responsiveResize(itemPane.width) + } } } + + component HeaderBackground: Rectangle { + anchors.fill: parent + anchors.leftMargin: -StudioTheme.Values.border + anchors.rightMargin: -StudioTheme.Values.border + z: parent.z - 1 + + color: StudioTheme.Values.themeToolbarBackground + border.color: StudioTheme.Values.themePanelBackground + border.width: StudioTheme.Values.border + } } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/UrlChooser.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/UrlChooser.qml index 0e42515c76c..8dadd1db2c2 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/UrlChooser.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/UrlChooser.qml @@ -451,7 +451,7 @@ Row { // Prefer hiding generated component files rather than other project files let listIndex = nameMap.get(item.fileName) let absPath = comboBox.listModel.get(listIndex).absoluteFilePath - if (absPath.includes("/GeneratedComponents/") || absPath.includes("/asset_imports/")) { + if (absPath.includes("/Generated/") || absPath.includes("/asset_imports/")) { comboBox.listModel.set(listIndex, { absoluteFilePath: item.absoluteFilePath, relativeFilePath: item.relativeFilePath, diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/CheckBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/CheckBox.qml index 573b78012f3..7e565e3a47d 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/CheckBox.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/CheckBox.qml @@ -14,7 +14,6 @@ T.CheckBox { // This property is used to indicate the global hover state property bool hover: control.hovered && control.enabled - property bool edit: false property alias actionIndicatorVisible: actionIndicator.visible property real __actionIndicatorWidth: control.style.actionIndicatorSize.width diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/MenuItem.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/MenuItem.qml index 07265e41a73..28372e41be1 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/MenuItem.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/MenuItem.qml @@ -9,7 +9,7 @@ import StudioTheme 1.0 as StudioTheme T.MenuItem { id: control - property alias shortcut: itemAction.shortcut + property alias shortcut: shortcutObserver.shortcutWorkaround property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle @@ -24,9 +24,6 @@ T.MenuItem { padding: 0 spacing: 0 horizontalPadding: control.style.contextMenuHorizontalPadding - action: Action { - id: itemAction - } contentItem: Item { Text { @@ -41,16 +38,23 @@ T.MenuItem { Text { id: shortcutLabel + anchors.right: parent.right anchors.verticalCenter: parent.verticalCenter text: shortcutObserver.nativeText + ? shortcutObserver.nativeText + : control.action + ? control.action.fakeShortcut ? control.action.fakeShortcut : "" + : "" font: control.font color: textLabel.color Shortcut { id: shortcutObserver - property int shortcutWorkaround: control.shortcut ?? 0 + + property int shortcutWorkaround: 0 sequence: shortcutObserver.shortcutWorkaround + enabled: false } } } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/PopupDialog.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/PopupDialog.qml index 40c35df27c7..821ba3a8491 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.Tool | Qt.WindowStaysOnTopHint + flags: Qt.FramelessWindowHint | Qt.Tool color: "transparent" onClosing: function (close) { @@ -286,10 +286,8 @@ QtObject { enabled: root.visible function onFocusWindowChanged(focusWindow) { - if (!focusWindow) { - root.close() + if (!focusWindow) return - } if (root.keepOpen) return diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/impl/ColorPicker.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/impl/ColorPicker.qml index cd525114268..fc2a0256644 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/impl/ColorPicker.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/impl/ColorPicker.qml @@ -13,6 +13,8 @@ Column { HSLA } + signal aboutToBeShown + property int mode: ColorPicker.Mode.HSVA property color color: "#303091" @@ -206,6 +208,8 @@ Column { target: root function onHueChanged() { gradientOverlay.requestPaint() } function onModeChanged() { gradientOverlay.requestPaint() } + function onAlphaChanged() { gradientOverlay.requestPaint() } + function onAboutToBeShown() { gradientOverlay.requestPaint() } } onPaint: { @@ -249,6 +253,7 @@ Column { function onColorInvalidated() { pickerCross.requestPaint() } function onColorChanged() { pickerCross.requestPaint() } function onModeChanged() { pickerCross.requestPaint() } + function onAboutToBeShown() { pickerCross.requestPaint() } } onPaint: { diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml index 1a04c8ebc30..69803cf3208 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/Values.qml @@ -243,16 +243,6 @@ QtObject { property real dialogButtonSpacing: 10 property real dialogButtonPadding: 4 - // Collection Editor - property real collectionItemTextSideMargin: 10 - property real collectionItemTextMargin: 5 - property real collectionItemTextPadding: 5 - property real collectionTableHorizontalMargin: 10 - property real collectionTableVerticalMargin: 10 - property real collectionCellMinimumWidth: 60 - property real collectionCellMinimumHeight: 20 - property real smallStatusIndicatorDiameter: 6 - // NEW NEW NEW readonly property int flowMargin: 7 readonly property int flowSpacing: 7 // Odd so cursor has a center location diff --git a/share/qtcreator/qmldesigner/qt4mcu/metadata.qml b/share/qtcreator/qmldesigner/qt4mcu/metadata.qml index b4ae840bddb..2df700e72ac 100644 --- a/share/qtcreator/qmldesigner/qt4mcu/metadata.qml +++ b/share/qtcreator/qmldesigner/qt4mcu/metadata.qml @@ -5,7 +5,7 @@ Metadata { id: metadataFile - defaultVersion: v26 + defaultVersion: v27 VersionData { id: v14 @@ -72,4 +72,10 @@ Metadata { name: "Qt for MCUs 2.6" path: "qul-26.qml" } + + VersionData { + id: v27 + name: "Qt for MCUs 2.7" + path: "qul-27.qml" + } } diff --git a/share/qtcreator/qmldesigner/qt4mcu/qul-27.qml b/share/qtcreator/qmldesigner/qt4mcu/qul-27.qml new file mode 100644 index 00000000000..6cb5640472a --- /dev/null +++ b/share/qtcreator/qmldesigner/qt4mcu/qul-27.qml @@ -0,0 +1,227 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +// new import: QUL.Studio.Components +// new types: ArcItem, Gradients, Basic and LinearGradient +// new properties: gradients related +// Layer Application renamed to ApplicationScreens + +VersionData { + name: "Qt for MCUs 2.7" + + bannedItems: [ + "QtQuick.AnimatedImage", + "QtQuick.Flow", + "QtQuick.FocusScope", + "QtQuick.Grid", + "QtQuick.GridView", + "QtQuick.PathView", + "QtQuick.TextEdit", + "QtQuick.TextInput", + "QtQuick.Controls", + "QtQuick.Controls.BusyIndicator", + "QtQuick.Controls.ButtonGroup", + "QtQuick.Controls.CheckDelegate", + "QtQuick.Controls.ComboBox", + "QtQuick.Controls.Container", + "QtQuick.Controls.DelayButton", + "QtQuick.Controls.Frame", + "QtQuick.Controls.GroupBox", + "QtQuick.Controls.ItemDelegate", + "QtQuick.Controls.Label", + "QtQuick.Controls.Page", + "QtQuick.Controls.PageIndicator", + "QtQuick.Controls.Pane", + "QtQuick.Controls.RadioDelegate", + "QtQuick.Controls.RangeSlider", + "QtQuick.Controls.RoundButton", + "QtQuick.Controls.ScrollView", + "QtQuick.Controls.SpinBox", + "QtQuick.Controls.StackView", + "QtQuick.Controls.SwipeDelegate", + "QtQuick.Controls.SwitchDelegate", + "QtQuick.Controls.TabBar", + "QtQuick.Controls.TabButton", + "QtQuick.Controls.TextArea", + "QtQuick.Controls.TextField", + "QtQuick.Controls.ToolBar", + "QtQuick.Controls.ToolButton", + "QtQuick.Controls.ToolSeparator", + "QtQuick.Controls.Tumbler", + "QtQuick.Shapes.ConicalGradient", + "QtQuick.Shapes.RadialGradient" + ] + + allowedImports: [ + "QtQuick", + "QtQuick.Controls", + "QtQuick.Shapes", + "QtQuick.Timeline", + "QtQuickUltralite.Extras", + "QtQuickUltralite.Layers", + "QtQuickUltralite.Profiling", + "QtQuickUltralite.Studio.Components" + ] + + bannedImports: [ + "FlowView", + "SimulinkConnector" + ] + + //ComplexProperty is not a type, it's just a way to handle bigger props + ComplexProperty { + prefix: "font" + bannedProperties: ["wordSpacing", "letterSpacing", "hintingPreference", + "kerning", "preferShaping", "capitalization", + "strikeout", "underline", "styleName"] + } + + QtQml.Timer { + bannedProperties: ["triggeredOnStart"] + } + + QtQuick.Item { + bannedProperties: ["layer", "opacity", "smooth", "antialiasing", + "baselineOffset", "focus", "activeFocusOnTab", + "rotation", "scale", "transformOrigin"] + } + + QtQuick.Rectangle { + bannedProperties: ["border"] + } + + QtQuick.Flickable { + bannedProperties: ["boundsMovement", "flickDeceleration", + "leftMargin", "rightMargin", "bottomMargin", "topMargin", + "originX", "originY", "pixelAligned", "pressDelay", "synchronousDrag"] + } + + QtQuick.MouseArea { + bannedProperties: ["propagateComposedEvents", "preventStealing", "cursorShape", + "scrollGestureEnabled", "drag", "acceptedButtons", "hoverEnabled"] + } + + QtQuick.Image { + allowChildren: false + allowedProperties: ["rotation", "scale", "transformOrigin"] + bannedProperties: ["mirror", "mipmap", "cache", "autoTransform", "asynchronous", + "sourceSize", "smooth"] + } + + QtQuick.BorderImage { + bannedProperties: ["asynchronous", "cache", "currentFrame", "frameCount", + "horizontalTileMode", "mirror", "progress", "smooth", "sourceSize", + "status", "verticalTileMode"] + } + + QtQuick.Text { + allowChildren: false + allowedProperties: ["rotation", "scale", "transformOrigin"] + bannedProperties: ["lineHeight", "lineHeightMode", "style", + "styleColor", "minimumPointSize", "minimumPixelSize", + "fontSizeMode", "renderType", "renderTypeQuality", "maximumLineCount"] + } + + QtQuick.Loader { + bannedProperties: ["asynchronous", "progress", "status"] + } + + //Padding is not an actual item, but rather set of properties in Text + Padding { + bannedProperties: ["bottomPadding", "topPadding", "leftPadding", "rightPadding"] + } + + QtQuick.Column { + bannedProperties: ["bottomPadding", "leftPadding", "rightPadding", "topPadding"] + } + + QtQuick.Row { + bannedProperties: ["bottomPadding", "leftPadding", "rightPadding", "topPadding", + "effectiveLayoutDirection", "layoutDirection"] + } + + QtQuick.ListView { + bannedProperties: ["cacheBuffer", "highlightRangeMode", "highlightMoveDuration", + "highlightResizeDuration", "preferredHighlightBegin", "layoutDirection", + "preferredHighlightEnd", "highlightFollowsCurrentItem", "keyNavigationWraps", + "snapMode", "highlightMoveVelocity", "highlightResizeVelocity"] + } + + QtQuick.Animation { + bannedProperties: ["paused"] + } + + QtQuick.AnimatedSprite { + allowedProperties: ["currentFrame", "frameCount", "paused"] + bannedProperties: ["finishBehavior", "frameRate", "frameSync", + "frameX", "frameY", "interpolate", "reverse"] + } + + //Quick Controls2 Items and properties: + + QtQuick.Controls.Control { + bannedProperties: ["focusPolicy", "hoverEnabled", "wheelEnabled"] + } + + QtQuick.Controls.AbstractButton { + bannedProperties: ["display", "autoExclusive", "icon"] + } + + QtQuick.Controls.ProgressBar { + bannedProperties: ["indeterminate"] + } + + QtQuick.Controls.Slider { + bannedProperties: ["live", "snapMode", "touchDragThreshold"] + } + + //Path and Shapes related: + + QtQuick.Path { + bannedProperties: ["scale", "pathElements"] + } + + QtQuick.PathArc { + bannedProperties: ["relativeX", "relativeY"] + } + + QtQuick.PathLine { + bannedProperties: ["relativeX", "relativeY"] + } + + QtQuick.PathMove { + bannedProperties: ["relativeX", "relativeY"] + } + + QtQuick.PathQuad { + bannedProperties: ["relativeX", "relativeY", + "relativeControlX", "relativeControlY"] + } + + QtQuick.PathCubic { + bannedProperties: ["relativeX", "relativeY", + "relativeControl1X", "relativeControl1Y", + "relativeControl2X", "relativeControl2Y"] + } + + QtQuick.PathElement { + //nothing + } + + QtQuick.PathSvg { + //nothing + } + + QtQuick.Shapes.Shape { + bannedProperties: ["asynchronous", "containsMode", "data", + "renderType", "status", "vendorExtensionsEnabled"] + } + + QtQuick.Shapes.ShapePath { + bannedProperties: ["dashOffset", "dashPattern", "strokeStyle"] + } + + QtQuickUltralite.Extras.ItemBuffer { + allowedProperties: ["rotation", "scale", "transformOrigin"] + } +} diff --git a/share/qtcreator/qmldesigner/studio_templates/files/javascript/wizard.json b/share/qtcreator/qmldesigner/studio_templates/files/javascript/wizard.json index 7ba90f75f6f..f70ed11c1da 100644 --- a/share/qtcreator/qmldesigner/studio_templates/files/javascript/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/files/javascript/wizard.json @@ -9,13 +9,46 @@ "icon": "file_javascript.png", "platformIndependent": true, + "options": + [ + { "key": "JSFile", "value": "%{Class}.%{JS: Util.preferredSuffix('application/javascript')}" }, + { "key": "ApplicationImport", "value": "%{QmlProjectName} 1.0" }, + { "key": "RootItem", "value": "%{JS: %{RootItemCB}.RootItem}" }, + { "key": "UseImportDefault", "value": "%{JS: false}" }, + { "key": "UseQtQuickControls2Default", "value": "%{JS: true}" } + ], "pages" : - [ + [ { - "trDisplayName": "Location", - "trShortTitle": "Location", - "typeId": "File" + "trDisplayName": "Define Class", + "trShortTitle": "Details", + "typeId": "Fields", + "data" : + [ + { + "name": "Class", + "trDisplayName": "Component name:", + "mandatory": true, + "type": "LineEdit", + "data": { + "validator": "(?:[A-Z_][a-zA-Z_0-9]*|)", + "fixup": "%{JS: '%{INPUT}'.charAt(0).toUpperCase() + '%{INPUT}'.slice(1) }" + } + }, + { + "name": "TargetPath", + "type": "PathChooser", + "trDisplayName": "Path:", + "mandatory": true, + "data": + { + "kind": "existingDirectory", + "basePath": "%{InitialPath}", + "path": "%{InitialPath}" + } + } + ] }, { "trDisplayName": "Options", @@ -34,21 +67,16 @@ } } ] - }, - { - "trDisplayName": "Project Management", - "trShortTitle": "Summary", - "typeId": "Summary" } ], "generators" : - [ + [ { "typeId": "File", "data": { "source": "file.js.tpl", - "target": "%{JS: Util.fileName('%{TargetPath}', '%{JS: Util.preferredSuffix('application/javascript')}')}", + "target": "%{TargetPath}/%{JSFile}", "openInEditor": true } } 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 a5d0d7539c0..a897fa421ae 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/application-3d/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/application-3d/wizard.json @@ -18,7 +18,7 @@ { "key": "ProjectPluginClassName", "value": "%{ProjectName}Plugin" }, { "key": "QmlProjectFileName", "value": "%{JS: Util.fileName('%{ProjectName}', 'qmlproject')}" }, { "key": "IsQt6Project", "value": "%{JS: value('QtQuickVersion') !== '2.15' }" }, - { "key": "AssetDir", "value": "GeneratedComponents" }, + { "key": "AssetDir", "value": "Generated" }, { "key": "ContentDir", "value": "%{ProjectName}Content" }, { "key": "ImportModuleName", "value": "%{ProjectName}" }, { "key": "UIClassName", "value": "Screen01" }, 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 5f2e5bfcafd..ccd522f7c84 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 @@ -18,7 +18,7 @@ { "key": "ProjectPluginClassName", "value": "%{ProjectName}Plugin" }, { "key": "QmlProjectFileName", "value": "%{JS: Util.fileName('%{ProjectName}', 'qmlproject')}" }, { "key": "IsQt6Project", "value": "%{JS: value('QtQuickVersion') !== '2.15' }" }, - { "key": "AssetDir", "value": "GeneratedComponents" }, + { "key": "AssetDir", "value": "Generated" }, { "key": "ContentDir", "value": "%{ProjectName}Content" }, { "key": "ImportModuleName", "value": "%{ProjectName}" }, { "key": "UIClassName", "value": "Screen01" }, diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/app_mcu.qmlproject.tpl b/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/app_mcu.qmlproject.tpl index f17d608f96c..7c4453752a6 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/app_mcu.qmlproject.tpl +++ b/share/qtcreator/qmldesigner/studio_templates/projects/application-mcu/app_mcu.qmlproject.tpl @@ -42,7 +42,9 @@ Project { "Controls", "ControlsTemplates", "Timeline", - "Shapes" + "Shapes", + "Profiling", + "StudioComponents" ] } @@ -55,8 +57,8 @@ Project { QDS.qtForMCUs: true QDS.qt6Project: true - QDS.qdsVersion: "4.3" - QDS.quickVersion: "6.5" + QDS.qdsVersion: "4.5" + QDS.quickVersion: "6.7" /* List of plugin directories passed to QML runtime */ importPaths: [ "imports" ] diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/application/wizard.json b/share/qtcreator/qmldesigner/studio_templates/projects/application/wizard.json index 41fe2df289a..f0f98c33315 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/application/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/application/wizard.json @@ -17,7 +17,7 @@ { "key": "ProjectPluginName", "value": "%{ProjectName}plugin" }, { "key": "ProjectPluginClassName", "value": "%{ProjectName}Plugin" }, { "key": "QmlProjectFileName", "value": "%{JS: Util.fileName('%{ProjectName}', 'qmlproject')}" }, - { "key": "AssetDir", "value": "GeneratedComponents" }, + { "key": "AssetDir", "value": "Generated" }, { "key": "ContentDir", "value": "%{ProjectName}Content" }, { "key": "ImportModuleName", "value": "%{ProjectName}" }, { "key": "UIClassName", "value": "Screen01" }, 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 ddaf5021543..c15d4dbf744 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/desktop-launcher/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/desktop-launcher/wizard.json @@ -17,7 +17,7 @@ { "key": "ProjectPluginName", "value": "%{ProjectName}plugin" }, { "key": "ProjectPluginClassName", "value": "%{ProjectName}Plugin" }, { "key": "QmlProjectFileName", "value": "%{JS: Util.fileName('%{ProjectName}', 'qmlproject')}" }, - { "key": "AssetDir", "value": "GeneratedComponents" }, + { "key": "AssetDir", "value": "Generated" }, { "key": "ContentDir", "value": "%{ProjectName}Content" }, { "key": "ImportModuleName", "value": "%{ProjectName}" }, { "key": "UIClassName", "value": "Screen01" }, 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 585a73aa907..d9e4b979083 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/mobile-scroll/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/mobile-scroll/wizard.json @@ -17,7 +17,7 @@ { "key": "ProjectPluginName", "value": "%{ProjectName}plugin" }, { "key": "ProjectPluginClassName", "value": "%{ProjectName}Plugin" }, { "key": "QmlProjectFileName", "value": "%{JS: Util.fileName('%{ProjectName}', 'qmlproject')}" }, - { "key": "AssetDir", "value": "GeneratedComponents" }, + { "key": "AssetDir", "value": "Generated" }, { "key": "ContentDir", "value": "%{ProjectName}Content" }, { "key": "ImportModuleName", "value": "%{ProjectName}" }, { "key": "UIClassName", "value": "Screen01" }, 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 a44a1429bee..910c32a42cf 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/mobile-stack/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/mobile-stack/wizard.json @@ -17,7 +17,7 @@ { "key": "ProjectPluginName", "value": "%{ProjectName}plugin" }, { "key": "ProjectPluginClassName", "value": "%{ProjectName}Plugin" }, { "key": "QmlProjectFileName", "value": "%{JS: Util.fileName('%{ProjectName}', 'qmlproject')}" }, - { "key": "AssetDir", "value": "GeneratedComponents" }, + { "key": "AssetDir", "value": "Generated" }, { "key": "ContentDir", "value": "%{ProjectName}Content" }, { "key": "ImportModuleName", "value": "%{ProjectName}" }, { "key": "IsQt6Project", "value": "%{JS: value('QtQuickVersion') !== '2.15' }" }, 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 b3f70a8b797..1f5be300e16 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/mobile-swipe/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/mobile-swipe/wizard.json @@ -17,7 +17,7 @@ { "key": "ProjectPluginName", "value": "%{ProjectName}plugin" }, { "key": "ProjectPluginClassName", "value": "%{ProjectName}Plugin" }, { "key": "QmlProjectFileName", "value": "%{JS: Util.fileName('%{ProjectName}', 'qmlproject')}" }, - { "key": "AssetDir", "value": "GeneratedComponents" }, + { "key": "AssetDir", "value": "Generated" }, { "key": "ContentDir", "value": "%{ProjectName}Content" }, { "key": "ImportModuleName", "value": "%{ProjectName}" }, { "key": "IsQt6Project", "value": "%{JS: value('QtQuickVersion') !== '2.15' }" }, 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 deleted file mode 100644 index 052b7abd015..00000000000 --- a/share/qtcreator/qmldesigner/studio_templates/projects/shared-plugin/name/Colors.json.tpl +++ /dev/null @@ -1,18 +0,0 @@ -[ - { - "colorCode": "#ff0000", - "name": "Red" - }, - { - "colorCode": "#00ff00", - "name": "Green" - }, - { - "colorCode": "#0000ff", - "name": "Blue" - }, - { - "colorCode": "#ffffff", - "name": "White" - } -] diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/shared-plugin/name/DataStore.qml.tpl b/share/qtcreator/qmldesigner/studio_templates/projects/shared-plugin/name/DataStore.qml.tpl deleted file mode 100644 index ca8b45ede08..00000000000 --- a/share/qtcreator/qmldesigner/studio_templates/projects/shared-plugin/name/DataStore.qml.tpl +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only - -pragma Singleton -import QtQuick 6.5 -import QtQuick.Studio.Utils 1.0 - -JsonListModel { - id: models - source: Qt.resolvedUrl("models.json") - - property ChildListModel book: ChildListModel { - modelName: "book" - } - - property JsonData backend: JsonData {} -} 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 deleted file mode 100644 index ca9c1736517..00000000000 --- a/share/qtcreator/qmldesigner/studio_templates/projects/shared-plugin/name/models.json.tpl +++ /dev/null @@ -1,56 +0,0 @@ -{ - "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/libs/3rdparty/sqlite/sqlite3.c b/src/libs/3rdparty/sqlite/sqlite3.c index 08c593e55c7..eaa24a13107 100644 --- a/src/libs/3rdparty/sqlite/sqlite3.c +++ b/src/libs/3rdparty/sqlite/sqlite3.c @@ -1,6 +1,6 @@ /****************************************************************************** ** This file is an amalgamation of many separate C source files from SQLite -** version 3.45.3. By combining all the individual C code files into this +** version 3.46.0. By combining all the individual C code files into this ** single large file, the entire code can be compiled as a single translation ** unit. This allows many compilers to do optimizations that would not be ** possible if the files were compiled separately. Performance improvements @@ -18,7 +18,7 @@ ** separate file. This file contains only code for the core SQLite library. ** ** The content in this amalgamation comes from Fossil check-in -** 8653b758870e6ef0c98d46b3ace27849054a. +** 96c92aba00c8375bc32fafcdf12429c58bd8. */ #define SQLITE_CORE 1 #define SQLITE_AMALGAMATION 1 @@ -459,9 +459,9 @@ extern "C" { ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ -#define SQLITE_VERSION "3.45.3" -#define SQLITE_VERSION_NUMBER 3045003 -#define SQLITE_SOURCE_ID "2024-04-15 13:34:05 8653b758870e6ef0c98d46b3ace27849054af85da891eb121e9aaa537f1e8355" +#define SQLITE_VERSION "3.46.0" +#define SQLITE_VERSION_NUMBER 3046000 +#define SQLITE_SOURCE_ID "2024-05-23 13:25:27 96c92aba00c8375bc32fafcdf12429c58bd8aabfcadab6683e35bbb9cdebf19e" /* ** CAPI3REF: Run-Time Library Version Numbers @@ -1077,11 +1077,11 @@ struct sqlite3_file { ** ** xLock() upgrades the database file lock. In other words, xLock() moves the ** database file lock in the direction NONE toward EXCLUSIVE. The argument to -** xLock() is always on of SHARED, RESERVED, PENDING, or EXCLUSIVE, never +** xLock() is always one of SHARED, RESERVED, PENDING, or EXCLUSIVE, never ** SQLITE_LOCK_NONE. If the database file lock is already at or above the ** requested lock, then the call to xLock() is a no-op. ** xUnlock() downgrades the database file lock to either SHARED or NONE. -* If the lock is already at or below the requested lock state, then the call +** If the lock is already at or below the requested lock state, then the call ** to xUnlock() is a no-op. ** The xCheckReservedLock() method checks whether any database connection, ** either in this process or in some other process, is holding a RESERVED, @@ -3618,8 +3618,8 @@ SQLITE_API int sqlite3_set_authorizer( #define SQLITE_RECURSIVE 33 /* NULL NULL */ /* -** CAPI3REF: Tracing And Profiling Functions -** METHOD: sqlite3 +** CAPI3REF: Deprecated Tracing And Profiling Functions +** DEPRECATED ** ** These routines are deprecated. Use the [sqlite3_trace_v2()] interface ** instead of the routines described here. @@ -7200,6 +7200,12 @@ SQLITE_API int sqlite3_autovacuum_pages( ** The exceptions defined in this paragraph might change in a future ** release of SQLite. ** +** Whether the update hook is invoked before or after the +** corresponding change is currently unspecified and may differ +** depending on the type of change. Do not rely on the order of the +** hook call with regards to the final result of the operation which +** triggers the hook. +** ** The update hook implementation must not do anything that will modify ** the database connection that invoked the update hook. Any actions ** to modify the database connection must be deferred until after the @@ -8670,7 +8676,7 @@ SQLITE_API int sqlite3_test_control(int op, ...); ** The sqlite3_keyword_count() interface returns the number of distinct ** keywords understood by SQLite. ** -** The sqlite3_keyword_name(N,Z,L) interface finds the N-th keyword and +** The sqlite3_keyword_name(N,Z,L) interface finds the 0-based N-th keyword and ** makes *Z point to that keyword expressed as UTF8 and writes the number ** of bytes in the keyword into *L. The string that *Z points to is not ** zero-terminated. The sqlite3_keyword_name(N,Z,L) routine returns @@ -10249,24 +10255,45 @@ SQLITE_API const char *sqlite3_vtab_collation(sqlite3_index_info*,int); **
  • ** ^(If the sqlite3_vtab_distinct() interface returns 2, that means ** that the query planner does not need the rows returned in any particular -** order, as long as rows with the same values in all "aOrderBy" columns -** are adjacent.)^ ^(Furthermore, only a single row for each particular -** combination of values in the columns identified by the "aOrderBy" field -** needs to be returned.)^ ^It is always ok for two or more rows with the same -** values in all "aOrderBy" columns to be returned, as long as all such rows -** are adjacent. ^The virtual table may, if it chooses, omit extra rows -** that have the same value for all columns identified by "aOrderBy". -** ^However omitting the extra rows is optional. +** order, as long as rows with the same values in all columns identified +** by "aOrderBy" are adjacent.)^ ^(Furthermore, when two or more rows +** contain the same values for all columns identified by "colUsed", all but +** one such row may optionally be omitted from the result.)^ +** The virtual table is not required to omit rows that are duplicates +** over the "colUsed" columns, but if the virtual table can do that without +** too much extra effort, it could potentially help the query to run faster. ** This mode is used for a DISTINCT query. **

  • -** ^(If the sqlite3_vtab_distinct() interface returns 3, that means -** that the query planner needs only distinct rows but it does need the -** rows to be sorted.)^ ^The virtual table implementation is free to omit -** rows that are identical in all aOrderBy columns, if it wants to, but -** it is not required to omit any rows. This mode is used for queries +** ^(If the sqlite3_vtab_distinct() interface returns 3, that means the +** virtual table must return rows in the order defined by "aOrderBy" as +** if the sqlite3_vtab_distinct() interface had returned 0. However if +** two or more rows in the result have the same values for all columns +** identified by "colUsed", then all but one such row may optionally be +** omitted.)^ Like when the return value is 2, the virtual table +** is not required to omit rows that are duplicates over the "colUsed" +** columns, but if the virtual table can do that without +** too much extra effort, it could potentially help the query to run faster. +** This mode is used for queries ** that have both DISTINCT and ORDER BY clauses. ** ** +**

    The following table summarizes the conditions under which the +** virtual table is allowed to set the "orderByConsumed" flag based on +** the value returned by sqlite3_vtab_distinct(). This table is a +** restatement of the previous four paragraphs: +** +** +** +**
    sqlite3_vtab_distinct() return value +** Rows are returned in aOrderBy order +** Rows with the same value in all aOrderBy columns are adjacent +** Duplicates over all colUsed columns may be omitted +**
    0yesyesno +**
    1noyesno +**
    2noyesyes +**
    3yesyesyes +**
    +** ** ^For the purposes of comparing virtual table output values to see if the ** values are same value for sorting purposes, two NULL values are considered ** to be the same. In other words, the comparison operator is "IS" @@ -12311,6 +12338,30 @@ SQLITE_API int sqlite3changegroup_schema(sqlite3_changegroup*, sqlite3*, const c */ SQLITE_API int sqlite3changegroup_add(sqlite3_changegroup*, int nData, void *pData); +/* +** CAPI3REF: Add A Single Change To A Changegroup +** METHOD: sqlite3_changegroup +** +** This function adds the single change currently indicated by the iterator +** passed as the second argument to the changegroup object. The rules for +** adding the change are just as described for [sqlite3changegroup_add()]. +** +** If the change is successfully added to the changegroup, SQLITE_OK is +** returned. Otherwise, an SQLite error code is returned. +** +** The iterator must point to a valid entry when this function is called. +** If it does not, SQLITE_ERROR is returned and no change is added to the +** changegroup. Additionally, the iterator must not have been opened with +** the SQLITE_CHANGESETAPPLY_INVERT flag. In this case SQLITE_ERROR is also +** returned. +*/ +SQLITE_API int sqlite3changegroup_add_change( + sqlite3_changegroup*, + sqlite3_changeset_iter* +); + + + /* ** CAPI3REF: Obtain A Composite Changeset From A Changegroup ** METHOD: sqlite3_changegroup @@ -13115,8 +13166,8 @@ struct Fts5PhraseIter { ** EXTENSION API FUNCTIONS ** ** xUserData(pFts): -** Return a copy of the context pointer the extension function was -** registered with. +** Return a copy of the pUserData pointer passed to the xCreateFunction() +** API when the extension function was registered. ** ** xColumnTotalSize(pFts, iCol, pnToken): ** If parameter iCol is less than zero, set output variable *pnToken @@ -14314,6 +14365,8 @@ struct fts5_api { # define SQLITE_OMIT_ALTERTABLE #endif +#define SQLITE_DIGIT_SEPARATOR '_' + /* ** Return true (non-zero) if the input is an integer that is too large ** to fit in 32-bits. This macro is used inside of various testcase() @@ -14606,8 +14659,8 @@ SQLITE_PRIVATE void sqlite3HashClear(Hash*); #define TK_TRUEFALSE 170 #define TK_ISNOT 171 #define TK_FUNCTION 172 -#define TK_UMINUS 173 -#define TK_UPLUS 174 +#define TK_UPLUS 173 +#define TK_UMINUS 174 #define TK_TRUTH 175 #define TK_REGISTER 176 #define TK_VECTOR 177 @@ -14616,8 +14669,9 @@ SQLITE_PRIVATE void sqlite3HashClear(Hash*); #define TK_ASTERISK 180 #define TK_SPAN 181 #define TK_ERROR 182 -#define TK_SPACE 183 -#define TK_ILLEGAL 184 +#define TK_QNUMBER 183 +#define TK_SPACE 184 +#define TK_ILLEGAL 185 /************** End of parse.h ***********************************************/ /************** Continuing where we left off in sqliteInt.h ******************/ @@ -14879,7 +14933,7 @@ typedef INT16_TYPE LogEst; # define SQLITE_PTRSIZE __SIZEOF_POINTER__ # elif defined(i386) || defined(__i386__) || defined(_M_IX86) || \ defined(_M_ARM) || defined(__arm__) || defined(__x86) || \ - (defined(__APPLE__) && defined(__POWERPC__)) || \ + (defined(__APPLE__) && defined(__ppc__)) || \ (defined(__TOS_AIX__) && !defined(__64BIT__)) # define SQLITE_PTRSIZE 4 # else @@ -15147,7 +15201,7 @@ SQLITE_PRIVATE u32 sqlite3WhereTrace; ** 0x00000010 Display sqlite3_index_info xBestIndex calls ** 0x00000020 Range an equality scan metrics ** 0x00000040 IN operator decisions -** 0x00000080 WhereLoop cost adjustements +** 0x00000080 WhereLoop cost adjustments ** 0x00000100 ** 0x00000200 Covering index decisions ** 0x00000400 OR optimization @@ -16296,6 +16350,7 @@ SQLITE_PRIVATE int sqlite3BtreeIntegrityCheck( sqlite3 *db, /* Database connection that is running the check */ Btree *p, /* The btree to be checked */ Pgno *aRoot, /* An array of root pages numbers for individual trees */ + sqlite3_value *aCnt, /* OUT: entry counts for each btree in aRoot[] */ int nRoot, /* Number of entries in aRoot[] */ int mxErr, /* Stop reporting errors after this many */ int *pnErr, /* OUT: Write number of errors seen to this variable */ @@ -16566,12 +16621,12 @@ typedef struct VdbeOpList VdbeOpList; #define OP_Vacuum 5 #define OP_VFilter 6 /* jump, synopsis: iplan=r[P3] zplan='P4' */ #define OP_VUpdate 7 /* synopsis: data=r[P3@P2] */ -#define OP_Init 8 /* jump, synopsis: Start at P2 */ +#define OP_Init 8 /* jump0, synopsis: Start at P2 */ #define OP_Goto 9 /* jump */ #define OP_Gosub 10 /* jump */ -#define OP_InitCoroutine 11 /* jump */ -#define OP_Yield 12 /* jump */ -#define OP_MustBeInt 13 /* jump */ +#define OP_InitCoroutine 11 /* jump0 */ +#define OP_Yield 12 /* jump0 */ +#define OP_MustBeInt 13 /* jump0 */ #define OP_Jump 14 /* jump */ #define OP_Once 15 /* jump */ #define OP_If 16 /* jump */ @@ -16579,22 +16634,22 @@ typedef struct VdbeOpList VdbeOpList; #define OP_IsType 18 /* jump, synopsis: if typeof(P1.P3) in P5 goto P2 */ #define OP_Not 19 /* same as TK_NOT, synopsis: r[P2]= !r[P1] */ #define OP_IfNullRow 20 /* jump, synopsis: if P1.nullRow then r[P3]=NULL, goto P2 */ -#define OP_SeekLT 21 /* jump, synopsis: key=r[P3@P4] */ -#define OP_SeekLE 22 /* jump, synopsis: key=r[P3@P4] */ -#define OP_SeekGE 23 /* jump, synopsis: key=r[P3@P4] */ -#define OP_SeekGT 24 /* jump, synopsis: key=r[P3@P4] */ +#define OP_SeekLT 21 /* jump0, synopsis: key=r[P3@P4] */ +#define OP_SeekLE 22 /* jump0, synopsis: key=r[P3@P4] */ +#define OP_SeekGE 23 /* jump0, synopsis: key=r[P3@P4] */ +#define OP_SeekGT 24 /* jump0, synopsis: key=r[P3@P4] */ #define OP_IfNotOpen 25 /* jump, synopsis: if( !csr[P1] ) goto P2 */ #define OP_IfNoHope 26 /* jump, synopsis: key=r[P3@P4] */ #define OP_NoConflict 27 /* jump, synopsis: key=r[P3@P4] */ #define OP_NotFound 28 /* jump, synopsis: key=r[P3@P4] */ #define OP_Found 29 /* jump, synopsis: key=r[P3@P4] */ -#define OP_SeekRowid 30 /* jump, synopsis: intkey=r[P3] */ +#define OP_SeekRowid 30 /* jump0, synopsis: intkey=r[P3] */ #define OP_NotExists 31 /* jump, synopsis: intkey=r[P3] */ -#define OP_Last 32 /* jump */ -#define OP_IfSmaller 33 /* jump */ +#define OP_Last 32 /* jump0 */ +#define OP_IfSizeBetween 33 /* jump */ #define OP_SorterSort 34 /* jump */ #define OP_Sort 35 /* jump */ -#define OP_Rewind 36 /* jump */ +#define OP_Rewind 36 /* jump0 */ #define OP_SorterNext 37 /* jump */ #define OP_Prev 38 /* jump */ #define OP_Next 39 /* jump */ @@ -16606,7 +16661,7 @@ typedef struct VdbeOpList VdbeOpList; #define OP_IdxGE 45 /* jump, synopsis: key=r[P3@P4] */ #define OP_RowSetRead 46 /* jump, synopsis: r[P3]=rowset(P1) */ #define OP_RowSetTest 47 /* jump, synopsis: if r[P3] in rowset(P1) goto P2 */ -#define OP_Program 48 /* jump */ +#define OP_Program 48 /* jump0 */ #define OP_FkIfZero 49 /* jump, synopsis: if fkctr[P1]==0 goto P2 */ #define OP_IsNull 50 /* jump, same as TK_ISNULL, synopsis: if r[P1]==NULL goto P2 */ #define OP_NotNull 51 /* jump, same as TK_NOTNULL, synopsis: if r[P1]!=NULL goto P2 */ @@ -16636,7 +16691,7 @@ typedef struct VdbeOpList VdbeOpList; #define OP_Null 75 /* synopsis: r[P2..P3]=NULL */ #define OP_SoftNull 76 /* synopsis: r[P1]=NULL */ #define OP_Blob 77 /* synopsis: r[P2]=P4 (len=P1) */ -#define OP_Variable 78 /* synopsis: r[P2]=parameter(P1,P4) */ +#define OP_Variable 78 /* synopsis: r[P2]=parameter(P1) */ #define OP_Move 79 /* synopsis: r[P2@P3]=r[P1@P3] */ #define OP_Copy 80 /* synopsis: r[P2@P3+1]=r[P1@P3+1] */ #define OP_SCopy 81 /* synopsis: r[P2]=r[P1] */ @@ -16760,14 +16815,15 @@ typedef struct VdbeOpList VdbeOpList; #define OPFLG_OUT2 0x10 /* out2: P2 is an output */ #define OPFLG_OUT3 0x20 /* out3: P3 is an output */ #define OPFLG_NCYCLE 0x40 /* ncycle:Cycles count against P1 */ +#define OPFLG_JUMP0 0x80 /* jump0: P2 might be zero */ #define OPFLG_INITIALIZER {\ /* 0 */ 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x41, 0x00,\ -/* 8 */ 0x01, 0x01, 0x01, 0x01, 0x03, 0x03, 0x01, 0x01,\ -/* 16 */ 0x03, 0x03, 0x01, 0x12, 0x01, 0x49, 0x49, 0x49,\ -/* 24 */ 0x49, 0x01, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49,\ -/* 32 */ 0x41, 0x01, 0x41, 0x41, 0x41, 0x01, 0x41, 0x41,\ +/* 8 */ 0x81, 0x01, 0x01, 0x81, 0x83, 0x83, 0x01, 0x01,\ +/* 16 */ 0x03, 0x03, 0x01, 0x12, 0x01, 0xc9, 0xc9, 0xc9,\ +/* 24 */ 0xc9, 0x01, 0x49, 0x49, 0x49, 0x49, 0xc9, 0x49,\ +/* 32 */ 0xc1, 0x01, 0x41, 0x41, 0xc1, 0x01, 0x41, 0x41,\ /* 40 */ 0x41, 0x41, 0x41, 0x26, 0x26, 0x41, 0x23, 0x0b,\ -/* 48 */ 0x01, 0x01, 0x03, 0x03, 0x0b, 0x0b, 0x0b, 0x0b,\ +/* 48 */ 0x81, 0x01, 0x03, 0x03, 0x0b, 0x0b, 0x0b, 0x0b,\ /* 56 */ 0x0b, 0x0b, 0x01, 0x03, 0x03, 0x03, 0x01, 0x41,\ /* 64 */ 0x01, 0x00, 0x00, 0x02, 0x02, 0x08, 0x00, 0x10,\ /* 72 */ 0x10, 0x10, 0x00, 0x10, 0x00, 0x10, 0x10, 0x00,\ @@ -16927,6 +16983,8 @@ SQLITE_PRIVATE RecordCompare sqlite3VdbeFindCompare(UnpackedRecord*); SQLITE_PRIVATE void sqlite3VdbeLinkSubProgram(Vdbe *, SubProgram *); SQLITE_PRIVATE int sqlite3VdbeHasSubProgram(Vdbe*); +SQLITE_PRIVATE void sqlite3MemSetArrayInt64(sqlite3_value *aMem, int iIdx, i64 val); + SQLITE_PRIVATE int sqlite3NotPureFunc(sqlite3_context*); #ifdef SQLITE_ENABLE_BYTECODE_VTAB SQLITE_PRIVATE int sqlite3VdbeBytecodeVtabInit(sqlite3*); @@ -17514,6 +17572,10 @@ struct FuncDefHash { }; #define SQLITE_FUNC_HASH(C,L) (((C)+(L))%SQLITE_FUNC_HASH_SZ) +#if defined(SQLITE_USER_AUTHENTICATION) +# warning "The SQLITE_USER_AUTHENTICATION extension is deprecated. \ + See ext/userauth/user-auth.txt for details." +#endif #ifdef SQLITE_USER_AUTHENTICATION /* ** Information held in the "sqlite3" database connection object and used @@ -17817,7 +17879,7 @@ struct sqlite3 { #define SQLITE_CursorHints 0x00000400 /* Add OP_CursorHint opcodes */ #define SQLITE_Stat4 0x00000800 /* Use STAT4 data */ /* TH3 expects this value ^^^^^^^^^^ to be 0x0000800. Don't change it */ -#define SQLITE_PushDown 0x00001000 /* The push-down optimization */ +#define SQLITE_PushDown 0x00001000 /* WHERE-clause push-down opt */ #define SQLITE_SimplifyJoin 0x00002000 /* Convert LEFT JOIN to JOIN */ #define SQLITE_SkipScan 0x00004000 /* Skip-scans */ #define SQLITE_PropagateConst 0x00008000 /* The constant propagation opt */ @@ -18390,8 +18452,7 @@ struct Table { #define TF_HasStored 0x00000040 /* Has one or more STORED columns */ #define TF_HasGenerated 0x00000060 /* Combo: HasVirtual + HasStored */ #define TF_WithoutRowid 0x00000080 /* No rowid. PRIMARY KEY is the key */ -#define TF_StatsUsed 0x00000100 /* Query planner decisions affected by - ** Index.aiRowLogEst[] values */ +#define TF_MaybeReanalyze 0x00000100 /* Maybe run ANALYZE on this table */ #define TF_NoVisibleRowid 0x00000200 /* No user-visible "rowid" column */ #define TF_OOOHidden 0x00000400 /* Out-of-Order hidden columns */ #define TF_HasNotNull 0x00000800 /* Contains NOT NULL constraints */ @@ -19191,10 +19252,12 @@ struct IdList { ** ** Union member validity: ** -** u1.zIndexedBy fg.isIndexedBy && !fg.isTabFunc -** u1.pFuncArg fg.isTabFunc && !fg.isIndexedBy -** u2.pIBIndex fg.isIndexedBy && !fg.isCte -** u2.pCteUse fg.isCte && !fg.isIndexedBy +** u1.zIndexedBy fg.isIndexedBy && !fg.isTabFunc +** u1.pFuncArg fg.isTabFunc && !fg.isIndexedBy +** u1.nRow !fg.isTabFunc && !fg.isIndexedBy +** +** u2.pIBIndex fg.isIndexedBy && !fg.isCte +** u2.pCteUse fg.isCte && !fg.isIndexedBy */ struct SrcItem { Schema *pSchema; /* Schema to which this item is fixed */ @@ -19222,6 +19285,7 @@ struct SrcItem { unsigned isOn :1; /* u3.pOn was once valid and non-NULL */ unsigned isSynthUsing :1; /* u3.pUsing is synthesized from NATURAL */ unsigned isNestedFrom :1; /* pSelect is a SF_NestedFrom subquery */ + unsigned rowidUsed :1; /* The ROWID of this table is referenced */ } fg; int iCursor; /* The VDBE cursor number used to access this table */ union { @@ -19232,6 +19296,7 @@ struct SrcItem { union { char *zIndexedBy; /* Identifier from "INDEXED BY " clause */ ExprList *pFuncArg; /* Arguments to table-valued-function */ + u32 nRow; /* Number of rows in a VALUES clause */ } u1; union { Index *pIBIndex; /* Index structure corresponding to u1.zIndexedBy */ @@ -19489,11 +19554,12 @@ struct Select { #define SF_View 0x0200000 /* SELECT statement is a view */ #define SF_NoopOrderBy 0x0400000 /* ORDER BY is ignored for this query */ #define SF_UFSrcCheck 0x0800000 /* Check pSrc as required by UPDATE...FROM */ -#define SF_PushDown 0x1000000 /* SELECT has be modified by push-down opt */ +#define SF_PushDown 0x1000000 /* Modified by WHERE-clause push-down opt */ #define SF_MultiPart 0x2000000 /* Has multiple incompatible PARTITIONs */ #define SF_CopyCte 0x4000000 /* SELECT statement is a copy of a CTE */ #define SF_OrderByReqd 0x8000000 /* The ORDER BY clause may not be omitted */ #define SF_UpdateFrom 0x10000000 /* Query originates with UPDATE FROM */ +#define SF_Correlated 0x20000000 /* True if references the outer context */ /* True if S exists and has SF_NestedFrom */ #define IsNestedFrom(S) ((S)!=0 && ((S)->selFlags&SF_NestedFrom)!=0) @@ -19733,6 +19799,7 @@ struct Parse { u8 disableLookaside; /* Number of times lookaside has been disabled */ u8 prepFlags; /* SQLITE_PREPARE_* flags */ u8 withinRJSubrtn; /* Nesting level for RIGHT JOIN body subroutines */ + u8 bHasWith; /* True if statement contains WITH */ #if defined(SQLITE_DEBUG) || defined(SQLITE_COVERAGE_TEST) u8 earlyCleanup; /* OOM inside sqlite3ParserAddCleanup() */ #endif @@ -20412,6 +20479,9 @@ struct Window { ** due to the SQLITE_SUBTYPE flag */ }; +SQLITE_PRIVATE Select *sqlite3MultiValues(Parse *pParse, Select *pLeft, ExprList *pRow); +SQLITE_PRIVATE void sqlite3MultiValuesEnd(Parse *pParse, Select *pVal); + #ifndef SQLITE_OMIT_WINDOWFUNC SQLITE_PRIVATE void sqlite3WindowDelete(sqlite3*, Window*); SQLITE_PRIVATE void sqlite3WindowUnlinkFromSelect(Window*); @@ -20729,6 +20799,7 @@ SQLITE_PRIVATE int sqlite3ErrorToParser(sqlite3*,int); SQLITE_PRIVATE void sqlite3Dequote(char*); SQLITE_PRIVATE void sqlite3DequoteExpr(Expr*); SQLITE_PRIVATE void sqlite3DequoteToken(Token*); +SQLITE_PRIVATE void sqlite3DequoteNumber(Parse*, Expr*); SQLITE_PRIVATE void sqlite3TokenInit(Token*,char*); SQLITE_PRIVATE int sqlite3KeywordCode(const unsigned char*, int); SQLITE_PRIVATE int sqlite3RunParser(Parse*, const char*); @@ -20759,7 +20830,7 @@ 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 int sqlite3ExprDeferredDelete(Parse*, Expr*); SQLITE_PRIVATE void sqlite3ExprUnmapAndDelete(Parse*, Expr*); SQLITE_PRIVATE ExprList *sqlite3ExprListAppend(Parse*,ExprList*,Expr*); SQLITE_PRIVATE ExprList *sqlite3ExprListAppendVector(Parse*,ExprList*,IdList*,Expr*); @@ -20982,12 +21053,10 @@ SQLITE_PRIVATE void sqlite3LeaveMutexAndCloseZombie(sqlite3*); SQLITE_PRIVATE u32 sqlite3IsTrueOrFalse(const char*); SQLITE_PRIVATE int sqlite3ExprIdToTrueFalse(Expr*); SQLITE_PRIVATE int sqlite3ExprTruthValue(const Expr*); -SQLITE_PRIVATE int sqlite3ExprIsConstant(Expr*); -SQLITE_PRIVATE int sqlite3ExprIsConstantNotJoin(Expr*); +SQLITE_PRIVATE int sqlite3ExprIsConstant(Parse*,Expr*); SQLITE_PRIVATE int sqlite3ExprIsConstantOrFunction(Expr*, u8); SQLITE_PRIVATE int sqlite3ExprIsConstantOrGroupBy(Parse*, Expr*, ExprList*); -SQLITE_PRIVATE int sqlite3ExprIsTableConstant(Expr*,int); -SQLITE_PRIVATE int sqlite3ExprIsSingleTableConstraint(Expr*,const SrcList*,int); +SQLITE_PRIVATE int sqlite3ExprIsSingleTableConstraint(Expr*,const SrcList*,int,int); #ifdef SQLITE_ENABLE_CURSOR_HINTS SQLITE_PRIVATE int sqlite3ExprContainsSubquery(Expr*); #endif @@ -21172,7 +21241,9 @@ SQLITE_PRIVATE void sqlite3ErrorWithMsg(sqlite3*, int, const char*,...); SQLITE_PRIVATE void sqlite3Error(sqlite3*,int); SQLITE_PRIVATE void sqlite3ErrorClear(sqlite3*); SQLITE_PRIVATE void sqlite3SystemError(sqlite3*,int); +#if !defined(SQLITE_OMIT_BLOB_LITERAL) SQLITE_PRIVATE void *sqlite3HexToBlob(sqlite3*, const char *z, int n); +#endif SQLITE_PRIVATE u8 sqlite3HexToInt(int h); SQLITE_PRIVATE int sqlite3TwoPartName(Parse *, Token *, Token *, Token **); @@ -24219,13 +24290,14 @@ struct DateTime { int tz; /* Timezone offset in minutes */ double s; /* Seconds */ char validJD; /* True (1) if iJD is valid */ - char rawS; /* Raw numeric value stored in s */ char validYMD; /* True (1) if Y,M,D are valid */ char validHMS; /* True (1) if h,m,s are valid */ - char validTZ; /* True (1) if tz is valid */ - char tzSet; /* Timezone was set explicitly */ - char isError; /* An overflow has occurred */ - char useSubsec; /* Display subsecond precision */ + char nFloor; /* Days to implement "floor" */ + unsigned rawS : 1; /* Raw numeric value stored in s */ + unsigned isError : 1; /* An overflow has occurred */ + unsigned useSubsec : 1; /* Display subsecond precision */ + unsigned isUtc : 1; /* Time is known to be UTC */ + unsigned isLocal : 1; /* Time is known to be localtime */ }; @@ -24323,6 +24395,8 @@ static int parseTimezone(const char *zDate, DateTime *p){ sgn = +1; }else if( c=='Z' || c=='z' ){ zDate++; + p->isLocal = 0; + p->isUtc = 1; goto zulu_time; }else{ return c!=0; @@ -24335,7 +24409,6 @@ static int parseTimezone(const char *zDate, DateTime *p){ p->tz = sgn*(nMn + nHr*60); zulu_time: while( sqlite3Isspace(*zDate) ){ zDate++; } - p->tzSet = 1; return *zDate!=0; } @@ -24379,7 +24452,6 @@ static int parseHhMmSs(const char *zDate, DateTime *p){ p->m = m; p->s = s + ms; if( parseTimezone(zDate, p) ) return 1; - p->validTZ = (p->tz!=0)?1:0; return 0; } @@ -24426,15 +24498,40 @@ static void computeJD(DateTime *p){ p->validJD = 1; if( p->validHMS ){ p->iJD += p->h*3600000 + p->m*60000 + (sqlite3_int64)(p->s*1000 + 0.5); - if( p->validTZ ){ + if( p->tz ){ p->iJD -= p->tz*60000; p->validYMD = 0; p->validHMS = 0; - p->validTZ = 0; + p->tz = 0; + p->isUtc = 1; + p->isLocal = 0; } } } +/* +** Given the YYYY-MM-DD information current in p, determine if there +** is day-of-month overflow and set nFloor to the number of days that +** would need to be subtracted from the date in order to bring the +** date back to the end of the month. +*/ +static void computeFloor(DateTime *p){ + assert( p->validYMD || p->isError ); + assert( p->D>=0 && p->D<=31 ); + assert( p->M>=0 && p->M<=12 ); + if( p->D<=28 ){ + p->nFloor = 0; + }else if( (1<M) & 0x15aa ){ + p->nFloor = 0; + }else if( p->M!=2 ){ + p->nFloor = (p->D==31); + }else if( p->Y%4!=0 || (p->Y%100==0 && p->Y%400!=0) ){ + p->nFloor = p->D - 28; + }else{ + p->nFloor = p->D - 29; + } +} + /* ** Parse dates of the form ** @@ -24473,12 +24570,16 @@ static int parseYyyyMmDd(const char *zDate, DateTime *p){ p->Y = neg ? -Y : Y; p->M = M; p->D = D; - if( p->validTZ ){ + computeFloor(p); + if( p->tz ){ computeJD(p); } return 0; } + +static void clearYMD_HMS_TZ(DateTime *p); /* Forward declaration */ + /* ** Set the time to the current time reported by the VFS. ** @@ -24488,6 +24589,9 @@ static int setDateTimeToCurrent(sqlite3_context *context, DateTime *p){ p->iJD = sqlite3StmtCurrentTime(context); if( p->iJD>0 ){ p->validJD = 1; + p->isUtc = 1; + p->isLocal = 0; + clearYMD_HMS_TZ(p); return 0; }else{ return 1; @@ -24626,7 +24730,7 @@ static void computeYMD_HMS(DateTime *p){ static void clearYMD_HMS_TZ(DateTime *p){ p->validYMD = 0; p->validHMS = 0; - p->validTZ = 0; + p->tz = 0; } #ifndef SQLITE_OMIT_LOCALTIME @@ -24758,7 +24862,7 @@ static int toLocaltime( p->validHMS = 1; p->validJD = 0; p->rawS = 0; - p->validTZ = 0; + p->tz = 0; p->isError = 0; return SQLITE_OK; } @@ -24778,12 +24882,12 @@ static const struct { float rLimit; /* Maximum NNN value for this transform */ float rXform; /* Constant used for this transform */ } aXformType[] = { - { 6, "second", 4.6427e+14, 1.0 }, - { 6, "minute", 7.7379e+12, 60.0 }, - { 4, "hour", 1.2897e+11, 3600.0 }, - { 3, "day", 5373485.0, 86400.0 }, - { 5, "month", 176546.0, 2592000.0 }, - { 4, "year", 14713.0, 31536000.0 }, + /* 0 */ { 6, "second", 4.6427e+14, 1.0 }, + /* 1 */ { 6, "minute", 7.7379e+12, 60.0 }, + /* 2 */ { 4, "hour", 1.2897e+11, 3600.0 }, + /* 3 */ { 3, "day", 5373485.0, 86400.0 }, + /* 4 */ { 5, "month", 176546.0, 30.0*86400.0 }, + /* 5 */ { 4, "year", 14713.0, 365.0*86400.0 }, }; /* @@ -24815,14 +24919,20 @@ static void autoAdjustDate(DateTime *p){ ** NNN.NNNN seconds ** NNN months ** NNN years +** +/-YYYY-MM-DD HH:MM:SS.SSS +** ceiling +** floor ** start of month ** start of year ** start of week ** start of day ** weekday N ** unixepoch +** auto ** localtime ** utc +** subsec +** subsecond ** ** Return 0 on success and 1 if there is any kind of error. If the error ** is in a system call (i.e. localtime()), then an error message is written @@ -24853,6 +24963,37 @@ static int parseModifier( } break; } + case 'c': { + /* + ** ceiling + ** + ** Resolve day-of-month overflow by rolling forward into the next + ** month. As this is the default action, this modifier is really + ** a no-op that is only included for symmetry. See "floor". + */ + if( sqlite3_stricmp(z, "ceiling")==0 ){ + computeJD(p); + clearYMD_HMS_TZ(p); + rc = 0; + p->nFloor = 0; + } + break; + } + case 'f': { + /* + ** floor + ** + ** Resolve day-of-month overflow by rolling back to the end of the + ** previous month. + */ + if( sqlite3_stricmp(z, "floor")==0 ){ + computeJD(p); + p->iJD -= p->nFloor*86400000; + clearYMD_HMS_TZ(p); + rc = 0; + } + break; + } case 'j': { /* ** julianday @@ -24879,7 +25020,9 @@ static int parseModifier( ** show local time. */ if( sqlite3_stricmp(z, "localtime")==0 && sqlite3NotPureFunc(pCtx) ){ - rc = toLocaltime(p, pCtx); + rc = p->isLocal ? SQLITE_OK : toLocaltime(p, pCtx); + p->isUtc = 0; + p->isLocal = 1; } break; } @@ -24904,7 +25047,7 @@ static int parseModifier( } #ifndef SQLITE_OMIT_LOCALTIME else if( sqlite3_stricmp(z, "utc")==0 && sqlite3NotPureFunc(pCtx) ){ - if( p->tzSet==0 ){ + if( p->isUtc==0 ){ i64 iOrigJD; /* Original localtime */ i64 iGuess; /* Guess at the corresponding utc time */ int cnt = 0; /* Safety to prevent infinite loop */ @@ -24927,7 +25070,8 @@ static int parseModifier( memset(p, 0, sizeof(*p)); p->iJD = iGuess; p->validJD = 1; - p->tzSet = 1; + p->isUtc = 1; + p->isLocal = 0; } rc = SQLITE_OK; } @@ -24947,7 +25091,7 @@ static int parseModifier( && r>=0.0 && r<7.0 && (n=(int)r)==r ){ sqlite3_int64 Z; computeYMD_HMS(p); - p->validTZ = 0; + p->tz = 0; p->validJD = 0; computeJD(p); Z = ((p->iJD + 129600000)/86400000) % 7; @@ -24987,7 +25131,7 @@ static int parseModifier( p->h = p->m = 0; p->s = 0.0; p->rawS = 0; - p->validTZ = 0; + p->tz = 0; p->validJD = 0; if( sqlite3_stricmp(z,"month")==0 ){ p->D = 1; @@ -25058,6 +25202,7 @@ static int parseModifier( x = p->M>0 ? (p->M-1)/12 : (p->M-12)/12; p->Y += x; p->M -= x*12; + computeFloor(p); computeJD(p); p->validHMS = 0; p->validYMD = 0; @@ -25104,11 +25249,12 @@ static int parseModifier( z += n; while( sqlite3Isspace(*z) ) z++; n = sqlite3Strlen30(z); - if( n>10 || n<3 ) break; + if( n<3 || n>10 ) break; if( sqlite3UpperToLower[(u8)z[n-1]]=='s' ) n--; computeJD(p); assert( rc==1 ); rRounder = r<0 ? -0.5 : +0.5; + p->nFloor = 0; for(i=0; iM += (int)r; x = p->M>0 ? (p->M-1)/12 : (p->M-12)/12; p->Y += x; p->M -= x*12; + computeFloor(p); p->validJD = 0; r -= (int)r; break; } case 5: { /* Special processing to add years */ int y = (int)r; - assert( strcmp(aXformType[i].zName,"year")==0 ); + assert( strcmp(aXformType[5].zName,"year")==0 ); computeYMD_HMS(p); + assert( p->M>=0 && p->M<=12 ); p->Y += y; + computeFloor(p); p->validJD = 0; r -= (int)r; break; @@ -25384,22 +25533,83 @@ static void dateFunc( } } +/* +** Compute the number of days after the most recent January 1. +** +** In other words, compute the zero-based day number for the +** current year: +** +** Jan01 = 0, Jan02 = 1, ..., Jan31 = 30, Feb01 = 31, ... +** Dec31 = 364 or 365. +*/ +static int daysAfterJan01(DateTime *pDate){ + DateTime jan01 = *pDate; + assert( jan01.validYMD ); + assert( jan01.validHMS ); + assert( pDate->validJD ); + jan01.validJD = 0; + jan01.M = 1; + jan01.D = 1; + computeJD(&jan01); + return (int)((pDate->iJD-jan01.iJD+43200000)/86400000); +} + +/* +** Return the number of days after the most recent Monday. +** +** In other words, return the day of the week according +** to this code: +** +** 0=Monday, 1=Tuesday, 2=Wednesday, ..., 6=Sunday. +*/ +static int daysAfterMonday(DateTime *pDate){ + assert( pDate->validJD ); + return (int)((pDate->iJD+43200000)/86400000) % 7; +} + +/* +** Return the number of days after the most recent Sunday. +** +** In other words, return the day of the week according +** to this code: +** +** 0=Sunday, 1=Monday, 2=Tues, ..., 6=Saturday +*/ +static int daysAfterSunday(DateTime *pDate){ + assert( pDate->validJD ); + return (int)((pDate->iJD+129600000)/86400000) % 7; +} + /* ** strftime( FORMAT, TIMESTRING, MOD, MOD, ...) ** ** Return a string described by FORMAT. Conversions as follows: ** -** %d day of month +** %d day of month 01-31 +** %e day of month 1-31 ** %f ** fractional seconds SS.SSS +** %F ISO date. YYYY-MM-DD +** %G ISO year corresponding to %V 0000-9999. +** %g 2-digit ISO year corresponding to %V 00-99 ** %H hour 00-24 -** %j day of year 000-366 +** %k hour 0-24 (leading zero converted to space) +** %I hour 01-12 +** %j day of year 001-366 ** %J ** julian day number +** %l hour 1-12 (leading zero converted to space) ** %m month 01-12 ** %M minute 00-59 +** %p "am" or "pm" +** %P "AM" or "PM" +** %R time as HH:MM ** %s seconds since 1970-01-01 ** %S seconds 00-59 -** %w day of week 0-6 Sunday==0 -** %W week of year 00-53 +** %T time as HH:MM:SS +** %u day of week 1-7 Monday==1, Sunday==7 +** %w day of week 0-6 Sunday==0, Monday==1 +** %U week of year 00-53 (First Sunday is start of week 01) +** %V week of year 01-53 (First week containing Thursday is week 01) +** %W week of year 00-53 (First Monday is start of week 01) ** %Y year 0000-9999 ** %% % */ @@ -25436,7 +25646,7 @@ static void strftimeFunc( sqlite3_str_appendf(&sRes, cf=='d' ? "%02d" : "%2d", x.D); break; } - case 'f': { + case 'f': { /* Fractional seconds. (Non-standard) */ double s = x.s; if( s>59.999 ) s = 59.999; sqlite3_str_appendf(&sRes, "%06.3f", s); @@ -25446,6 +25656,21 @@ static void strftimeFunc( sqlite3_str_appendf(&sRes, "%04d-%02d-%02d", x.Y, x.M, x.D); break; } + case 'G': /* Fall thru */ + case 'g': { + DateTime y = x; + assert( y.validJD ); + /* Move y so that it is the Thursday in the same week as x */ + y.iJD += (3 - daysAfterMonday(&x))*86400000; + y.validYMD = 0; + computeYMD(&y); + if( cf=='g' ){ + sqlite3_str_appendf(&sRes, "%02d", y.Y%100); + }else{ + sqlite3_str_appendf(&sRes, "%04d", y.Y); + } + break; + } case 'H': case 'k': { sqlite3_str_appendf(&sRes, cf=='H' ? "%02d" : "%2d", x.h); @@ -25459,25 +25684,11 @@ static void strftimeFunc( sqlite3_str_appendf(&sRes, cf=='I' ? "%02d" : "%2d", h); break; } - case 'W': /* Fall thru */ - case 'j': { - int nDay; /* Number of days since 1st day of year */ - DateTime y = x; - y.validJD = 0; - y.M = 1; - y.D = 1; - computeJD(&y); - nDay = (int)((x.iJD-y.iJD+43200000)/86400000); - 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); - }else{ - sqlite3_str_appendf(&sRes,"%03d",nDay+1); - } + case 'j': { /* Day of year. Jan01==1, Jan02==2, and so forth */ + sqlite3_str_appendf(&sRes,"%03d",daysAfterJan01(&x)+1); break; } - case 'J': { + case 'J': { /* Julian day number. (Non-standard) */ sqlite3_str_appendf(&sRes,"%.16g",x.iJD/86400000.0); break; } @@ -25520,13 +25731,33 @@ static void strftimeFunc( sqlite3_str_appendf(&sRes,"%02d:%02d:%02d", x.h, x.m, (int)x.s); break; } - case 'u': /* Fall thru */ - case 'w': { - char c = (char)(((x.iJD+129600000)/86400000) % 7) + '0'; + case 'u': /* Day of week. 1 to 7. Monday==1, Sunday==7 */ + case 'w': { /* Day of week. 0 to 6. Sunday==0, Monday==1 */ + char c = (char)daysAfterSunday(&x) + '0'; if( c=='0' && cf=='u' ) c = '7'; sqlite3_str_appendchar(&sRes, 1, c); break; } + case 'U': { /* Week num. 00-53. First Sun of the year is week 01 */ + sqlite3_str_appendf(&sRes,"%02d", + (daysAfterJan01(&x)-daysAfterSunday(&x)+7)/7); + break; + } + case 'V': { /* Week num. 01-53. First week with a Thur is week 01 */ + DateTime y = x; + /* Adjust y so that is the Thursday in the same week as x */ + assert( y.validJD ); + y.iJD += (3 - daysAfterMonday(&x))*86400000; + y.validYMD = 0; + computeYMD(&y); + sqlite3_str_appendf(&sRes,"%02d", daysAfterJan01(&y)/7+1); + break; + } + case 'W': { /* Week num. 00-53. First Mon of the year is week 01 */ + sqlite3_str_appendf(&sRes,"%02d", + (daysAfterJan01(&x)-daysAfterMonday(&x)+7)/7); + break; + } case 'Y': { sqlite3_str_appendf(&sRes,"%04d",x.Y); break; @@ -25673,9 +25904,7 @@ static void timediffFunc( d1.iJD = d2.iJD - d1.iJD; d1.iJD += (u64)1486995408 * (u64)100000; } - d1.validYMD = 0; - d1.validHMS = 0; - d1.validTZ = 0; + clearYMD_HMS_TZ(&d1); computeYMD_HMS(&d1); sqlite3StrAccumInit(&sRes, 0, 0, 0, 100); sqlite3_str_appendf(&sRes, "%c%04d-%02d-%02d %02d:%02d:%06.3f", @@ -25744,6 +25973,36 @@ static void currentTimeFunc( } #endif +#if !defined(SQLITE_OMIT_DATETIME_FUNCS) && defined(SQLITE_DEBUG) +/* +** datedebug(...) +** +** This routine returns JSON that describes the internal DateTime object. +** Used for debugging and testing only. Subject to change. +*/ +static void datedebugFunc( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + DateTime x; + if( isDate(context, argc, argv, &x)==0 ){ + char *zJson; + zJson = sqlite3_mprintf( + "{iJD:%lld,Y:%d,M:%d,D:%d,h:%d,m:%d,tz:%d," + "s:%.3f,validJD:%d,validYMS:%d,validHMS:%d," + "nFloor:%d,rawS:%d,isError:%d,useSubsec:%d," + "isUtc:%d,isLocal:%d}", + x.iJD, x.Y, x.M, x.D, x.h, x.m, x.tz, + x.s, x.validJD, x.validYMD, x.validHMS, + x.nFloor, x.rawS, x.isError, x.useSubsec, + x.isUtc, x.isLocal); + sqlite3_result_text(context, zJson, -1, sqlite3_free); + } +} +#endif /* !SQLITE_OMIT_DATETIME_FUNCS && SQLITE_DEBUG */ + + /* ** This function registered all of the above C functions as SQL ** functions. This should be the only routine in this file with @@ -25759,6 +26018,9 @@ SQLITE_PRIVATE void sqlite3RegisterDateTimeFunctions(void){ PURE_DATE(datetime, -1, 0, 0, datetimeFunc ), PURE_DATE(strftime, -1, 0, 0, strftimeFunc ), PURE_DATE(timediff, 2, 0, 0, timediffFunc ), +#ifdef SQLITE_DEBUG + PURE_DATE(datedebug, -1, 0, 0, datedebugFunc ), +#endif DFUNCTION(current_time, 0, 0, 0, ctimeFunc ), DFUNCTION(current_timestamp, 0, 0, 0, ctimestampFunc), DFUNCTION(current_date, 0, 0, 0, cdateFunc ), @@ -30174,6 +30436,24 @@ static void sqlite3MallocAlarm(int nByte){ sqlite3_mutex_enter(mem0.mutex); } +#ifdef SQLITE_DEBUG +/* +** This routine is called whenever an out-of-memory condition is seen, +** It's only purpose to to serve as a breakpoint for gdb or similar +** code debuggers when working on out-of-memory conditions, for example +** caused by PRAGMA hard_heap_limit=N. +*/ +static SQLITE_NOINLINE void test_oom_breakpoint(u64 n){ + static u64 nOomFault = 0; + nOomFault += n; + /* The assert() is never reached in a human lifetime. It is here mostly + ** to prevent code optimizers from optimizing out this function. */ + assert( (nOomFault>>32) < 0xffffffff ); +} +#else +# define test_oom_breakpoint(X) /* No-op for production builds */ +#endif + /* ** Do a memory allocation with statistics and alarms. Assume the ** lock is already held. @@ -30200,6 +30480,7 @@ static void mallocWithAlarm(int n, void **pp){ if( mem0.hardLimit ){ nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED); if( nUsed >= mem0.hardLimit - nFull ){ + test_oom_breakpoint(1); *pp = 0; return; } @@ -30488,6 +30769,7 @@ SQLITE_PRIVATE void *sqlite3Realloc(void *pOld, u64 nBytes){ sqlite3MallocAlarm(nDiff); if( mem0.hardLimit>0 && nUsed >= mem0.hardLimit - nDiff ){ sqlite3_mutex_leave(mem0.mutex); + test_oom_breakpoint(1); return 0; } } @@ -31390,13 +31672,14 @@ SQLITE_API void sqlite3_str_vappendf( } exp = s.iDP-1; - if( xtype==etGENERIC && precision>0 ) precision--; /* ** If the field type is etGENERIC, then convert to either etEXP ** or etFLOAT, as appropriate. */ if( xtype==etGENERIC ){ + assert( precision>0 ); + precision--; flag_rtz = !flag_alternateform; if( exp<-4 || exp>precision ){ xtype = etEXP; @@ -31712,9 +31995,13 @@ SQLITE_API void sqlite3_str_vappendf( sqlite3_str_appendall(pAccum, pItem->zAlias); }else{ Select *pSel = pItem->pSelect; - assert( pSel!=0 ); + assert( pSel!=0 ); /* Because of tag-20240424-1 */ if( pSel->selFlags & SF_NestedFrom ){ sqlite3_str_appendf(pAccum, "(join-%u)", pSel->selId); + }else if( pSel->selFlags & SF_MultiValue ){ + assert( !pItem->fg.isTabFunc && !pItem->fg.isIndexedBy ); + sqlite3_str_appendf(pAccum, "%u-ROW VALUES CLAUSE", + pItem->u1.nRow); }else{ sqlite3_str_appendf(pAccum, "(subquery-%u)", pSel->selId); } @@ -32491,8 +32778,10 @@ SQLITE_PRIVATE void sqlite3TreeViewSrcList(TreeView *pView, const SrcList *pSrc) x.printfFlags |= SQLITE_PRINTF_INTERNAL; sqlite3_str_appendf(&x, "{%d:*} %!S", pItem->iCursor, pItem); if( pItem->pTab ){ - sqlite3_str_appendf(&x, " tab=%Q nCol=%d ptr=%p used=%llx", - pItem->pTab->zName, pItem->pTab->nCol, pItem->pTab, pItem->colUsed); + sqlite3_str_appendf(&x, " tab=%Q nCol=%d ptr=%p used=%llx%s", + pItem->pTab->zName, pItem->pTab->nCol, pItem->pTab, + pItem->colUsed, + pItem->fg.rowidUsed ? "+rowid" : ""); } if( (pItem->fg.jointype & (JT_LEFT|JT_RIGHT))==(JT_LEFT|JT_RIGHT) ){ sqlite3_str_appendf(&x, " FULL-OUTER-JOIN"); @@ -32532,12 +32821,14 @@ SQLITE_PRIVATE void sqlite3TreeViewSrcList(TreeView *pView, const SrcList *pSrc) sqlite3TreeViewIdList(pView, pItem->u3.pUsing, (--n)>0, "USING"); } if( pItem->pSelect ){ + sqlite3TreeViewPush(&pView, i+1nSrc); if( pItem->pTab ){ Table *pTab = pItem->pTab; sqlite3TreeViewColumnList(pView, pTab->aCol, pTab->nCol, 1); } assert( (int)pItem->fg.isNestedFrom == IsNestedFrom(pItem->pSelect) ); sqlite3TreeViewSelect(pView, pItem->pSelect, (--n)>0); + sqlite3TreeViewPop(&pView); } if( pItem->fg.isTabFunc ){ sqlite3TreeViewExprList(pView, pItem->u1.pFuncArg, 0, "func-args:"); @@ -32641,7 +32932,7 @@ SQLITE_PRIVATE void sqlite3TreeViewSelect(TreeView *pView, const Select *p, u8 m sqlite3TreeViewItem(pView, "LIMIT", (n--)>0); sqlite3TreeViewExpr(pView, p->pLimit->pLeft, p->pLimit->pRight!=0); if( p->pLimit->pRight ){ - sqlite3TreeViewItem(pView, "OFFSET", (n--)>0); + sqlite3TreeViewItem(pView, "OFFSET", 0); sqlite3TreeViewExpr(pView, p->pLimit->pRight, 0); sqlite3TreeViewPop(&pView); } @@ -34942,6 +35233,44 @@ SQLITE_PRIVATE void sqlite3DequoteExpr(Expr *p){ sqlite3Dequote(p->u.zToken); } +/* +** Expression p is a QNUMBER (quoted number). Dequote the value in p->u.zToken +** and set the type to INTEGER or FLOAT. "Quoted" integers or floats are those +** that contain '_' characters that must be removed before further processing. +*/ +SQLITE_PRIVATE void sqlite3DequoteNumber(Parse *pParse, Expr *p){ + assert( p!=0 || pParse->db->mallocFailed ); + if( p ){ + const char *pIn = p->u.zToken; + char *pOut = p->u.zToken; + int bHex = (pIn[0]=='0' && (pIn[1]=='x' || pIn[1]=='X')); + int iValue; + assert( p->op==TK_QNUMBER ); + p->op = TK_INTEGER; + do { + if( *pIn!=SQLITE_DIGIT_SEPARATOR ){ + *pOut++ = *pIn; + if( *pIn=='e' || *pIn=='E' || *pIn=='.' ) p->op = TK_FLOAT; + }else{ + if( (bHex==0 && (!sqlite3Isdigit(pIn[-1]) || !sqlite3Isdigit(pIn[1]))) + || (bHex==1 && (!sqlite3Isxdigit(pIn[-1]) || !sqlite3Isxdigit(pIn[1]))) + ){ + sqlite3ErrorMsg(pParse, "unrecognized token: \"%s\"", p->u.zToken); + } + } + }while( *pIn++ ); + if( bHex ) p->op = TK_INTEGER; + + /* tag-20240227-a: If after dequoting, the number is an integer that + ** fits in 32 bits, then it must be converted into EP_IntValue. Other + ** parts of the code expect this. See also tag-20240227-b. */ + if( p->op==TK_INTEGER && sqlite3GetInt32(p->u.zToken, &iValue) ){ + p->u.iValue = iValue; + p->flags |= EP_IntValue; + } + } +} + /* ** If the input token p is quoted, try to adjust the token to remove ** the quotes. This is not always possible: @@ -36881,7 +37210,7 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){ /* 30 */ "SeekRowid" OpHelp("intkey=r[P3]"), /* 31 */ "NotExists" OpHelp("intkey=r[P3]"), /* 32 */ "Last" OpHelp(""), - /* 33 */ "IfSmaller" OpHelp(""), + /* 33 */ "IfSizeBetween" OpHelp(""), /* 34 */ "SorterSort" OpHelp(""), /* 35 */ "Sort" OpHelp(""), /* 36 */ "Rewind" OpHelp(""), @@ -36926,7 +37255,7 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){ /* 75 */ "Null" OpHelp("r[P2..P3]=NULL"), /* 76 */ "SoftNull" OpHelp("r[P1]=NULL"), /* 77 */ "Blob" OpHelp("r[P2]=P4 (len=P1)"), - /* 78 */ "Variable" OpHelp("r[P2]=parameter(P1,P4)"), + /* 78 */ "Variable" OpHelp("r[P2]=parameter(P1)"), /* 79 */ "Move" OpHelp("r[P2@P3]=r[P1@P3]"), /* 80 */ "Copy" OpHelp("r[P2@P3+1]=r[P1@P3+1]"), /* 81 */ "SCopy" OpHelp("r[P2]=r[P1]"), @@ -39324,8 +39653,12 @@ static int unixLogErrorAtLine( ** available, the error message will often be an empty string. Not a ** huge problem. Incorrectly concluding that the GNU version is available ** could lead to a segfault though. + ** + ** Forum post 3f13857fa4062301 reports that the Android SDK may use + ** int-type return, depending on its version. */ -#if defined(STRERROR_R_CHAR_P) || defined(__USE_GNU) +#if (defined(STRERROR_R_CHAR_P) || defined(__USE_GNU)) \ + && !defined(ANDROID) && !defined(__ANDROID__) zErr = # endif strerror_r(iErrno, aErr, sizeof(aErr)-1); @@ -44423,12 +44756,19 @@ static int unixOpen( rc = SQLITE_READONLY_DIRECTORY; }else if( errno!=EISDIR && isReadWrite ){ /* Failed to open the file for read/write access. Try read-only. */ + UnixUnusedFd *pReadonly = 0; flags &= ~(SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE); openFlags &= ~(O_RDWR|O_CREAT); flags |= SQLITE_OPEN_READONLY; openFlags |= O_RDONLY; isReadonly = 1; - fd = robust_open(zName, openFlags, openMode); + pReadonly = findReusableFd(zName, flags); + if( pReadonly ){ + fd = pReadonly->fd; + sqlite3_free(pReadonly); + }else{ + fd = robust_open(zName, openFlags, openMode); + } } } if( fd<0 ){ @@ -69879,6 +70219,7 @@ struct IntegrityCk { StrAccum errMsg; /* Accumulate the error message text here */ u32 *heap; /* Min-heap used for analyzing cell coverage */ sqlite3 *db; /* Database connection running the check */ + i64 nRow; /* Number of rows visited in current tree */ }; /* @@ -70353,8 +70694,47 @@ int corruptPageError(int lineno, MemPage *p){ # define SQLITE_CORRUPT_PAGE(pMemPage) SQLITE_CORRUPT_PGNO(pMemPage->pgno) #endif +/* Default value for SHARED_LOCK_TRACE macro if shared-cache is disabled +** or if the lock tracking is disabled. This is always the value for +** release builds. +*/ +#define SHARED_LOCK_TRACE(X,MSG,TAB,TYPE) /*no-op*/ + #ifndef SQLITE_OMIT_SHARED_CACHE +#if 0 +/* ^---- Change to 1 and recompile to enable shared-lock tracing +** for debugging purposes. +** +** Print all shared-cache locks on a BtShared. Debugging use only. +*/ +static void sharedLockTrace( + BtShared *pBt, + const char *zMsg, + int iRoot, + int eLockType +){ + BtLock *pLock; + if( iRoot>0 ){ + printf("%s-%p %u%s:", zMsg, pBt, iRoot, eLockType==READ_LOCK?"R":"W"); + }else{ + printf("%s-%p:", zMsg, pBt); + } + for(pLock=pBt->pLock; pLock; pLock=pLock->pNext){ + printf(" %p/%u%s", pLock->pBtree, pLock->iTable, + pLock->eLock==READ_LOCK ? "R" : "W"); + while( pLock->pNext && pLock->pBtree==pLock->pNext->pBtree ){ + pLock = pLock->pNext; + printf(",%u%s", pLock->iTable, pLock->eLock==READ_LOCK ? "R" : "W"); + } + } + printf("\n"); + fflush(stdout); +} +#undef SHARED_LOCK_TRACE +#define SHARED_LOCK_TRACE(X,MSG,TAB,TYPE) sharedLockTrace(X,MSG,TAB,TYPE) +#endif /* Shared-lock tracing */ + #ifdef SQLITE_DEBUG /* **** This function is only used as part of an assert() statement. *** @@ -70431,6 +70811,8 @@ static int hasSharedCacheTableLock( iTab = iRoot; } + SHARED_LOCK_TRACE(pBtree->pBt,"hasLock",iRoot,eLockType); + /* Search for the required lock. Either a write-lock on root-page iTab, a ** write-lock on the schema table, or (if the client is reading) a ** read-lock on iTab will suffice. Return 1 if any of these are found. */ @@ -70564,6 +70946,8 @@ static int setSharedCacheTableLock(Btree *p, Pgno iTable, u8 eLock){ BtLock *pLock = 0; BtLock *pIter; + SHARED_LOCK_TRACE(pBt,"setLock", iTable, eLock); + assert( sqlite3BtreeHoldsMutex(p) ); assert( eLock==READ_LOCK || eLock==WRITE_LOCK ); assert( p->db!=0 ); @@ -70631,6 +71015,8 @@ static void clearAllSharedCacheTableLocks(Btree *p){ assert( p->sharable || 0==*ppIter ); assert( p->inTrans>0 ); + SHARED_LOCK_TRACE(pBt, "clearAllLocks", 0, 0); + while( *ppIter ){ BtLock *pLock = *ppIter; assert( (pBt->btsFlags & BTS_EXCLUSIVE)==0 || pBt->pWriter==pLock->pBtree ); @@ -70669,6 +71055,9 @@ static void clearAllSharedCacheTableLocks(Btree *p){ */ static void downgradeAllSharedCacheTableLocks(Btree *p){ BtShared *pBt = p->pBt; + + SHARED_LOCK_TRACE(pBt, "downgradeLocks", 0, 0); + if( pBt->pWriter==p ){ BtLock *pLock; pBt->pWriter = 0; @@ -75282,9 +75671,12 @@ static int accessPayload( if( pCur->aOverflow==0 || nOvfl*(int)sizeof(Pgno) > sqlite3MallocSize(pCur->aOverflow) ){ - Pgno *aNew = (Pgno*)sqlite3Realloc( - pCur->aOverflow, nOvfl*2*sizeof(Pgno) - ); + Pgno *aNew; + if( sqlite3FaultSim(413) ){ + aNew = 0; + }else{ + aNew = (Pgno*)sqlite3Realloc(pCur->aOverflow, nOvfl*2*sizeof(Pgno)); + } if( aNew==0 ){ return SQLITE_NOMEM_BKPT; }else{ @@ -75294,6 +75686,12 @@ static int accessPayload( memset(pCur->aOverflow, 0, nOvfl*sizeof(Pgno)); pCur->curFlags |= BTCF_ValidOvfl; }else{ + /* Sanity check the validity of the overflow page cache */ + assert( pCur->aOverflow[0]==nextPage + || pCur->aOverflow[0]==0 + || CORRUPT_DB ); + assert( pCur->aOverflow[0]!=0 || pCur->aOverflow[offset/ovflSize]==0 ); + /* If the overflow page-list cache has been allocated and the ** entry for the first required overflow page is valid, skip ** directly to it. @@ -75775,6 +76173,23 @@ SQLITE_PRIVATE int sqlite3BtreeFirst(BtCursor *pCur, int *pRes){ return rc; } +#ifdef SQLITE_DEBUG +/* The cursors is CURSOR_VALID and has BTCF_AtLast set. Verify that +** this flags are true for a consistent database. +** +** This routine is is called from within assert() statements only. +** It is an internal verification routine and does not appear in production +** builds. +*/ +static int cursorIsAtLastEntry(BtCursor *pCur){ + int ii; + for(ii=0; iiiPage; ii++){ + if( pCur->aiIdx[ii]!=pCur->apPage[ii]->nCell ) return 0; + } + return pCur->ix==pCur->pPage->nCell-1 && pCur->pPage->leaf!=0; +} +#endif + /* Move the cursor to the last entry in the table. Return SQLITE_OK ** on success. Set *pRes to 0 if the cursor actually points to something ** or set *pRes to 1 if the table is empty. @@ -75803,18 +76218,7 @@ SQLITE_PRIVATE int sqlite3BtreeLast(BtCursor *pCur, int *pRes){ /* If the cursor already points to the last entry, this is a no-op. */ if( CURSOR_VALID==pCur->eState && (pCur->curFlags & BTCF_AtLast)!=0 ){ -#ifdef SQLITE_DEBUG - /* This block serves to assert() that the cursor really does point - ** to the last entry in the b-tree. */ - int ii; - for(ii=0; iiiPage; ii++){ - assert( pCur->aiIdx[ii]==pCur->apPage[ii]->nCell ); - } - assert( pCur->ix==pCur->pPage->nCell-1 || CORRUPT_DB ); - testcase( pCur->ix!=pCur->pPage->nCell-1 ); - /* ^-- dbsqlfuzz b92b72e4de80b5140c30ab71372ca719b8feb618 */ - assert( pCur->pPage->leaf ); -#endif + assert( cursorIsAtLastEntry(pCur) || CORRUPT_DB ); *pRes = 0; return SQLITE_OK; } @@ -75867,6 +76271,7 @@ SQLITE_PRIVATE int sqlite3BtreeTableMoveto( } if( pCur->info.nKeycurFlags & BTCF_AtLast)!=0 ){ + assert( cursorIsAtLastEntry(pCur) || CORRUPT_DB ); *pRes = -1; return SQLITE_OK; } @@ -76333,10 +76738,10 @@ SQLITE_PRIVATE i64 sqlite3BtreeRowCountEst(BtCursor *pCur){ assert( cursorOwnsBtShared(pCur) ); assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); - /* Currently this interface is only called by the OP_IfSmaller - ** opcode, and it that case the cursor will always be valid and - ** will always point to a leaf node. */ - if( NEVER(pCur->eState!=CURSOR_VALID) ) return -1; + /* Currently this interface is only called by the OP_IfSizeBetween + ** opcode and the OP_Count opcode with P3=1. In either case, + ** the cursor will always be valid unless the btree is empty. */ + if( pCur->eState!=CURSOR_VALID ) return 0; if( NEVER(pCur->pPage->leaf==0) ) return -1; n = pCur->pPage->nCell; @@ -78467,7 +78872,7 @@ static int balance_nonroot( ** table-interior, index-leaf, or index-interior). */ if( pOld->aData[0]!=apOld[0]->aData[0] ){ - rc = SQLITE_CORRUPT_BKPT; + rc = SQLITE_CORRUPT_PAGE(pOld); goto balance_cleanup; } @@ -78491,7 +78896,7 @@ static int balance_nonroot( memset(&b.szCell[b.nCell], 0, sizeof(b.szCell[0])*(limit+pOld->nOverflow)); if( pOld->nOverflow>0 ){ if( NEVER(limitaiOvfl[0]) ){ - rc = SQLITE_CORRUPT_BKPT; + rc = SQLITE_CORRUPT_PAGE(pOld); goto balance_cleanup; } limit = pOld->aiOvfl[0]; @@ -79134,7 +79539,7 @@ static int anotherValidCursor(BtCursor *pCur){ && pOther->eState==CURSOR_VALID && pOther->pPage==pCur->pPage ){ - return SQLITE_CORRUPT_BKPT; + return SQLITE_CORRUPT_PAGE(pCur->pPage); } } return SQLITE_OK; @@ -79194,7 +79599,7 @@ static int balance(BtCursor *pCur){ /* The page being written is not a root page, and there is currently ** more than one reference to it. This only happens if the page is one ** of its own ancestor pages. Corruption. */ - rc = SQLITE_CORRUPT_BKPT; + rc = SQLITE_CORRUPT_PAGE(pPage); }else{ MemPage * const pParent = pCur->apPage[iPage-1]; int const iIdx = pCur->aiIdx[iPage-1]; @@ -79358,7 +79763,7 @@ static SQLITE_NOINLINE int btreeOverwriteOverflowCell( rc = btreeGetPage(pBt, ovflPgno, &pPage, 0); if( rc ) return rc; if( sqlite3PagerPageRefcount(pPage->pDbPage)!=1 || pPage->isInit ){ - rc = SQLITE_CORRUPT_BKPT; + rc = SQLITE_CORRUPT_PAGE(pPage); }else{ if( iOffset+ovflPageSize<(u32)nTotal ){ ovflPgno = get4byte(pPage->aData); @@ -79386,7 +79791,7 @@ static int btreeOverwriteCell(BtCursor *pCur, const BtreePayload *pX){ if( pCur->info.pPayload + pCur->info.nLocal > pPage->aDataEnd || pCur->info.pPayload < pPage->aData + pPage->cellOffset ){ - return SQLITE_CORRUPT_BKPT; + return SQLITE_CORRUPT_PAGE(pPage); } if( pCur->info.nLocal==nTotal ){ /* The entire cell is local */ @@ -79467,7 +79872,7 @@ SQLITE_PRIVATE int sqlite3BtreeInsert( ** Which can only happen if the SQLITE_NoSchemaError flag was set when ** the schema was loaded. This cannot be asserted though, as a user might ** set the flag, load the schema, and then unset the flag. */ - return SQLITE_CORRUPT_BKPT; + return SQLITE_CORRUPT_PGNO(pCur->pgnoRoot); } } @@ -79590,7 +79995,7 @@ SQLITE_PRIVATE int sqlite3BtreeInsert( if( pPage->nFree<0 ){ if( NEVER(pCur->eState>CURSOR_INVALID) ){ /* ^^^^^--- due to the moveToRoot() call above */ - rc = SQLITE_CORRUPT_BKPT; + rc = SQLITE_CORRUPT_PAGE(pPage); }else{ rc = btreeComputeFreeSpace(pPage); } @@ -79632,7 +80037,7 @@ SQLITE_PRIVATE int sqlite3BtreeInsert( CellInfo info; assert( idx>=0 ); if( idx>=pPage->nCell ){ - return SQLITE_CORRUPT_BKPT; + return SQLITE_CORRUPT_PAGE(pPage); } rc = sqlite3PagerWrite(pPage->pDbPage); if( rc ){ @@ -79659,10 +80064,10 @@ SQLITE_PRIVATE int sqlite3BtreeInsert( ** necessary to add the PTRMAP_OVERFLOW1 pointer-map entry. */ assert( rc==SQLITE_OK ); /* clearCell never fails when nLocal==nPayload */ if( oldCell < pPage->aData+pPage->hdrOffset+10 ){ - return SQLITE_CORRUPT_BKPT; + return SQLITE_CORRUPT_PAGE(pPage); } if( oldCell+szNew > pPage->aDataEnd ){ - return SQLITE_CORRUPT_BKPT; + return SQLITE_CORRUPT_PAGE(pPage); } memcpy(oldCell, newCell, szNew); return SQLITE_OK; @@ -79764,7 +80169,7 @@ SQLITE_PRIVATE int sqlite3BtreeTransferRow(BtCursor *pDest, BtCursor *pSrc, i64 nIn = pSrc->info.nLocal; aIn = pSrc->info.pPayload; if( aIn+nIn>pSrc->pPage->aDataEnd ){ - return SQLITE_CORRUPT_BKPT; + return SQLITE_CORRUPT_PAGE(pSrc->pPage); } nRem = pSrc->info.nPayload; if( nIn==nRem && nInpPage->maxLocal ){ @@ -79789,7 +80194,7 @@ SQLITE_PRIVATE int sqlite3BtreeTransferRow(BtCursor *pDest, BtCursor *pSrc, i64 if( nRem>nIn ){ if( aIn+nIn+4>pSrc->pPage->aDataEnd ){ - return SQLITE_CORRUPT_BKPT; + return SQLITE_CORRUPT_PAGE(pSrc->pPage); } ovflIn = get4byte(&pSrc->info.pPayload[nIn]); } @@ -79885,7 +80290,7 @@ SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor *pCur, u8 flags){ assert( rc!=SQLITE_OK || CORRUPT_DB || pCur->eState==CURSOR_VALID ); if( rc || pCur->eState!=CURSOR_VALID ) return rc; }else{ - return SQLITE_CORRUPT_BKPT; + return SQLITE_CORRUPT_PGNO(pCur->pgnoRoot); } } assert( pCur->eState==CURSOR_VALID ); @@ -79894,14 +80299,14 @@ SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor *pCur, u8 flags){ iCellIdx = pCur->ix; pPage = pCur->pPage; if( pPage->nCell<=iCellIdx ){ - return SQLITE_CORRUPT_BKPT; + return SQLITE_CORRUPT_PAGE(pPage); } pCell = findCell(pPage, iCellIdx); if( pPage->nFree<0 && btreeComputeFreeSpace(pPage) ){ - return SQLITE_CORRUPT_BKPT; + return SQLITE_CORRUPT_PAGE(pPage); } if( pCell<&pPage->aCellIdx[pPage->nCell] ){ - return SQLITE_CORRUPT_BKPT; + return SQLITE_CORRUPT_PAGE(pPage); } /* If the BTREE_SAVEPOSITION bit is on, then the cursor position must @@ -79992,7 +80397,7 @@ SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor *pCur, u8 flags){ n = pCur->pPage->pgno; } pCell = findCell(pLeaf, pLeaf->nCell-1); - if( pCell<&pLeaf->aData[4] ) return SQLITE_CORRUPT_BKPT; + if( pCell<&pLeaf->aData[4] ) return SQLITE_CORRUPT_PAGE(pLeaf); nCell = pLeaf->xCellSize(pLeaf, pCell); assert( MX_CELL_SIZE(pBt) >= nCell ); pTmp = pBt->pTmpSpace; @@ -80108,7 +80513,7 @@ static int btreeCreateTable(Btree *p, Pgno *piTable, int createTabFlags){ */ sqlite3BtreeGetMeta(p, BTREE_LARGEST_ROOT_PAGE, &pgnoRoot); if( pgnoRoot>btreePagecount(pBt) ){ - return SQLITE_CORRUPT_BKPT; + return SQLITE_CORRUPT_PGNO(pgnoRoot); } pgnoRoot++; @@ -80156,7 +80561,7 @@ static int btreeCreateTable(Btree *p, Pgno *piTable, int createTabFlags){ } rc = ptrmapGet(pBt, pgnoRoot, &eType, &iPtrPage); if( eType==PTRMAP_ROOTPAGE || eType==PTRMAP_FREEPAGE ){ - rc = SQLITE_CORRUPT_BKPT; + rc = SQLITE_CORRUPT_PGNO(pgnoRoot); } if( rc!=SQLITE_OK ){ releasePage(pRoot); @@ -80246,14 +80651,14 @@ static int clearDatabasePage( assert( sqlite3_mutex_held(pBt->mutex) ); if( pgno>btreePagecount(pBt) ){ - return SQLITE_CORRUPT_BKPT; + return SQLITE_CORRUPT_PGNO(pgno); } rc = getAndInitPage(pBt, pgno, &pPage, 0); if( rc ) return rc; if( (pBt->openFlags & BTREE_SINGLE)==0 && sqlite3PagerPageRefcount(pPage->pDbPage) != (1 + (pgno==1)) ){ - rc = SQLITE_CORRUPT_BKPT; + rc = SQLITE_CORRUPT_PAGE(pPage); goto cleardatabasepage_out; } hdr = pPage->hdrOffset; @@ -80357,7 +80762,7 @@ static int btreeDropTable(Btree *p, Pgno iTable, int *piMoved){ assert( p->inTrans==TRANS_WRITE ); assert( iTable>=2 ); if( iTable>btreePagecount(pBt) ){ - return SQLITE_CORRUPT_BKPT; + return SQLITE_CORRUPT_PGNO(iTable); } rc = sqlite3BtreeClearTable(p, iTable, 0); @@ -80951,6 +81356,9 @@ static int checkTreePage( ** number of cells on the page. */ nCell = get2byte(&data[hdr+3]); assert( pPage->nCell==nCell ); + if( pPage->leaf || pPage->intKey==0 ){ + pCheck->nRow += nCell; + } /* EVIDENCE-OF: R-23882-45353 The cell pointer array of a b-tree page ** immediately follows the b-tree page header. */ @@ -81062,6 +81470,7 @@ static int checkTreePage( btreeHeapInsert(heap, (pc<<16)|(pc+size-1)); } } + assert( heap!=0 ); /* Add the freeblocks to the min-heap ** ** EVIDENCE-OF: R-20690-50594 The second field of the b-tree page header @@ -81161,6 +81570,7 @@ SQLITE_PRIVATE int sqlite3BtreeIntegrityCheck( sqlite3 *db, /* Database connection that is running the check */ Btree *p, /* The btree to be checked */ Pgno *aRoot, /* An array of root pages numbers for individual trees */ + Mem *aCnt, /* Memory cells to write counts for each tree to */ int nRoot, /* Number of entries in aRoot[] */ int mxErr, /* Stop reporting errors after this many */ int *pnErr, /* OUT: Write number of errors seen to this variable */ @@ -81174,7 +81584,9 @@ SQLITE_PRIVATE int sqlite3BtreeIntegrityCheck( int bPartial = 0; /* True if not checking all btrees */ int bCkFreelist = 1; /* True to scan the freelist */ VVA_ONLY( int nRef ); + assert( nRoot>0 ); + assert( aCnt!=0 ); /* aRoot[0]==0 means this is a partial check */ if( aRoot[0]==0 ){ @@ -81247,15 +81659,18 @@ SQLITE_PRIVATE int sqlite3BtreeIntegrityCheck( testcase( pBt->db->flags & SQLITE_CellSizeCk ); pBt->db->flags &= ~(u64)SQLITE_CellSizeCk; for(i=0; (int)iautoVacuum && aRoot[i]>1 && !bPartial ){ - checkPtrmap(&sCheck, aRoot[i], PTRMAP_ROOTPAGE, 0); - } + if( pBt->autoVacuum && aRoot[i]>1 && !bPartial ){ + checkPtrmap(&sCheck, aRoot[i], PTRMAP_ROOTPAGE, 0); + } #endif - sCheck.v0 = aRoot[i]; - checkTreePage(&sCheck, aRoot[i], ¬Used, LARGEST_INT64); + sCheck.v0 = aRoot[i]; + checkTreePage(&sCheck, aRoot[i], ¬Used, LARGEST_INT64); + } + sqlite3MemSetArrayInt64(aCnt, i, sCheck.nRow); } pBt->db->flags = savedDbFlags; @@ -83310,6 +83725,13 @@ SQLITE_PRIVATE void sqlite3VdbeMemSetInt64(Mem *pMem, i64 val){ } } +/* +** Set the iIdx'th entry of array aMem[] to contain integer value val. +*/ +SQLITE_PRIVATE void sqlite3MemSetArrayInt64(sqlite3_value *aMem, int iIdx, i64 val){ + sqlite3VdbeMemSetInt64(&aMem[iIdx], val); +} + /* A no-op destructor */ SQLITE_PRIVATE void sqlite3NoopDestructor(void *p){ UNUSED_PARAMETER(p); } @@ -83998,14 +84420,20 @@ static int valueFromExpr( } /* Handle negative integers in a single step. This is needed in the - ** case when the value is -9223372036854775808. - */ - if( op==TK_UMINUS - && (pExpr->pLeft->op==TK_INTEGER || pExpr->pLeft->op==TK_FLOAT) ){ - pExpr = pExpr->pLeft; - op = pExpr->op; - negInt = -1; - zNeg = "-"; + ** case when the value is -9223372036854775808. Except - do not do this + ** for hexadecimal literals. */ + if( op==TK_UMINUS ){ + Expr *pLeft = pExpr->pLeft; + if( (pLeft->op==TK_INTEGER || pLeft->op==TK_FLOAT) ){ + if( ExprHasProperty(pLeft, EP_IntValue) + || pLeft->u.zToken[0]!='0' || (pLeft->u.zToken[1] & ~0x20)!='X' + ){ + pExpr = pLeft; + op = pExpr->op; + negInt = -1; + zNeg = "-"; + } + } } if( op==TK_STRING || op==TK_FLOAT || op==TK_INTEGER ){ @@ -84014,12 +84442,26 @@ static int valueFromExpr( if( ExprHasProperty(pExpr, EP_IntValue) ){ sqlite3VdbeMemSetInt64(pVal, (i64)pExpr->u.iValue*negInt); }else{ - zVal = sqlite3MPrintf(db, "%s%s", zNeg, pExpr->u.zToken); - if( zVal==0 ) goto no_mem; - sqlite3ValueSetStr(pVal, -1, zVal, SQLITE_UTF8, SQLITE_DYNAMIC); + i64 iVal; + if( op==TK_INTEGER && 0==sqlite3DecOrHexToI64(pExpr->u.zToken, &iVal) ){ + sqlite3VdbeMemSetInt64(pVal, iVal*negInt); + }else{ + zVal = sqlite3MPrintf(db, "%s%s", zNeg, pExpr->u.zToken); + if( zVal==0 ) goto no_mem; + sqlite3ValueSetStr(pVal, -1, zVal, SQLITE_UTF8, SQLITE_DYNAMIC); + } } - if( (op==TK_INTEGER || op==TK_FLOAT ) && affinity==SQLITE_AFF_BLOB ){ - sqlite3ValueApplyAffinity(pVal, SQLITE_AFF_NUMERIC, SQLITE_UTF8); + if( affinity==SQLITE_AFF_BLOB ){ + if( op==TK_FLOAT ){ + assert( pVal && pVal->z && pVal->flags==(MEM_Str|MEM_Term) ); + sqlite3AtoF(pVal->z, &pVal->u.r, pVal->n, SQLITE_UTF8); + pVal->flags = MEM_Real; + }else if( op==TK_INTEGER ){ + /* This case is required by -9223372036854775808 and other strings + ** that look like integers but cannot be handled by the + ** sqlite3DecOrHexToI64() call above. */ + sqlite3ValueApplyAffinity(pVal, SQLITE_AFF_NUMERIC, SQLITE_UTF8); + } }else{ sqlite3ValueApplyAffinity(pVal, affinity, SQLITE_UTF8); } @@ -84289,17 +84731,17 @@ SQLITE_PRIVATE int sqlite3Stat4Column( sqlite3_value **ppVal /* OUT: Extracted value */ ){ u32 t = 0; /* a column type code */ - int nHdr; /* Size of the header in the record */ - int iHdr; /* Next unread header byte */ - int iField; /* Next unread data byte */ - int szField = 0; /* Size of the current data field */ + u32 nHdr; /* Size of the header in the record */ + u32 iHdr; /* Next unread header byte */ + i64 iField; /* Next unread data byte */ + u32 szField = 0; /* Size of the current data field */ int i; /* Column index */ u8 *a = (u8*)pRec; /* Typecast byte array */ Mem *pMem = *ppVal; /* Write result into this Mem object */ assert( iCol>0 ); iHdr = getVarint32(a, nHdr); - if( nHdr>nRec || iHdr>=nHdr ) return SQLITE_CORRUPT_BKPT; + if( nHdr>(u32)nRec || iHdr>=nHdr ) return SQLITE_CORRUPT_BKPT; iField = nHdr; for(i=0; i<=iCol; i++){ iHdr += getVarint32(&a[iHdr], t); @@ -85334,6 +85776,15 @@ static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){ assert( aLabel!=0 ); /* True because of tag-20230419-1 */ pOp->p2 = aLabel[ADDR(pOp->p2)]; } + + /* OPFLG_JUMP opcodes never have P2==0, though OPFLG_JUMP0 opcodes + ** might */ + assert( pOp->p2>0 + || (sqlite3OpcodeProperty[pOp->opcode] & OPFLG_JUMP0)!=0 ); + + /* Jumps never go off the end of the bytecode array */ + assert( pOp->p2nOp + || (sqlite3OpcodeProperty[pOp->opcode] & OPFLG_JUMP)==0 ); break; } } @@ -87741,7 +88192,7 @@ SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){ /* Check for immediate foreign key violations. */ if( p->rc==SQLITE_OK || (p->errorAction==OE_Fail && !isSpecialError) ){ - sqlite3VdbeCheckFk(p, 0); + (void)sqlite3VdbeCheckFk(p, 0); } /* If the auto-commit flag is set and this is the only active writer @@ -88911,17 +89362,15 @@ SQLITE_PRIVATE int sqlite3IntFloatCompare(i64 i, double r){ return (xr); }else{ i64 y; - double s; if( r<-9223372036854775808.0 ) return +1; if( r>=9223372036854775808.0 ) return -1; y = (i64)r; if( iy ) return +1; - s = (double)i; - testcase( doubleLt(s,r) ); - testcase( doubleLt(r,s) ); - testcase( doubleEq(r,s) ); - return (sr); + testcase( doubleLt(((double)i),r) ); + testcase( doubleLt(r,((double)i)) ); + testcase( doubleEq(r,((double)i)) ); + return (((double)i)r); } } @@ -92329,7 +92778,6 @@ SQLITE_API int sqlite3_stmt_scanstatus_v2( } if( flags & SQLITE_SCANSTAT_COMPLEX ){ idx = iScan; - pScan = &p->aScan[idx]; }else{ /* If the COMPLEX flag is clear, then this function must ignore any ** ScanStatus structures with ScanStatus.addrLoop set to 0. */ @@ -92342,6 +92790,8 @@ SQLITE_API int sqlite3_stmt_scanstatus_v2( } } if( idx>=p->nScan ) return 1; + assert( pScan==0 || pScan==&p->aScan[idx] ); + pScan = &p->aScan[idx]; switch( iScanStatusOp ){ case SQLITE_SCANSTAT_NLOOP: { @@ -93790,7 +94240,7 @@ case OP_Return: { /* in1 */ ** ** See also: EndCoroutine */ -case OP_InitCoroutine: { /* jump */ +case OP_InitCoroutine: { /* jump0 */ assert( pOp->p1>0 && pOp->p1<=(p->nMem+1 - p->nCursor) ); assert( pOp->p2>=0 && pOp->p2nOp ); assert( pOp->p3>=0 && pOp->p3nOp ); @@ -93813,7 +94263,9 @@ jump_to_p2: ** ** The instruction at the address in register P1 is a Yield. ** Jump to the P2 parameter of that Yield. -** After the jump, register P1 becomes undefined. +** After the jump, the value register P1 is left with a value +** such that subsequent OP_Yields go back to the this same +** OP_EndCoroutine instruction. ** ** See also: InitCoroutine */ @@ -93825,8 +94277,8 @@ case OP_EndCoroutine: { /* in1 */ pCaller = &aOp[pIn1->u.i]; assert( pCaller->opcode==OP_Yield ); assert( pCaller->p2>=0 && pCaller->p2nOp ); + pIn1->u.i = (int)(pOp - p->aOp) - 1; pOp = &aOp[pCaller->p2 - 1]; - pIn1->flags = MEM_Undefined; break; } @@ -93843,7 +94295,7 @@ case OP_EndCoroutine: { /* in1 */ ** ** See also: InitCoroutine */ -case OP_Yield: { /* in1, jump */ +case OP_Yield: { /* in1, jump0 */ int pcDest; pIn1 = &aMem[pOp->p1]; assert( VdbeMemDynamic(pIn1)==0 ); @@ -94173,19 +94625,15 @@ case OP_Blob: { /* out2 */ break; } -/* Opcode: Variable P1 P2 * P4 * -** Synopsis: r[P2]=parameter(P1,P4) +/* Opcode: Variable P1 P2 * * * +** Synopsis: r[P2]=parameter(P1) ** ** Transfer the values of bound parameter P1 into register P2 -** -** If the parameter is named, then its name appears in P4. -** The P4 value is used by sqlite3_bind_parameter_name(). */ case OP_Variable: { /* out2 */ Mem *pVar; /* Value being transferred */ assert( pOp->p1>0 && pOp->p1<=p->nVar ); - assert( pOp->p4.z==0 || pOp->p4.z==sqlite3VListNumToName(p->pVList,pOp->p1) ); pVar = &p->aVar[pOp->p1 - 1]; if( sqlite3VdbeMemTooBig(pVar) ){ goto too_big; @@ -94706,7 +95154,7 @@ case OP_AddImm: { /* in1 */ ** without data loss, then jump immediately to P2, or if P2==0 ** raise an SQLITE_MISMATCH exception. */ -case OP_MustBeInt: { /* jump, in1 */ +case OP_MustBeInt: { /* jump0, in1 */ pIn1 = &aMem[pOp->p1]; if( (pIn1->flags & MEM_Int)==0 ){ applyAffinity(pIn1, SQLITE_AFF_NUMERIC, encoding); @@ -94747,7 +95195,7 @@ case OP_RealAffinity: { /* in1 */ } #endif -#ifndef SQLITE_OMIT_CAST +#if !defined(SQLITE_OMIT_CAST) && !defined(SQLITE_OMIT_ANALYZE) /* Opcode: Cast P1 P2 * * * ** Synopsis: affinity(r[P1]) ** @@ -96319,11 +96767,16 @@ case OP_MakeRecord: { switch( len ){ default: zPayload[7] = (u8)(v&0xff); v >>= 8; zPayload[6] = (u8)(v&0xff); v >>= 8; + /* no break */ deliberate_fall_through case 6: zPayload[5] = (u8)(v&0xff); v >>= 8; zPayload[4] = (u8)(v&0xff); v >>= 8; + /* no break */ deliberate_fall_through case 4: zPayload[3] = (u8)(v&0xff); v >>= 8; + /* no break */ deliberate_fall_through case 3: zPayload[2] = (u8)(v&0xff); v >>= 8; + /* no break */ deliberate_fall_through case 2: zPayload[1] = (u8)(v&0xff); v >>= 8; + /* no break */ deliberate_fall_through case 1: zPayload[0] = (u8)(v&0xff); } zPayload += len; @@ -97242,7 +97695,8 @@ case OP_SequenceTest: { ** is the only cursor opcode that works with a pseudo-table. ** ** P3 is the number of fields in the records that will be stored by -** the pseudo-table. +** the pseudo-table. If P2 is 0 or negative then the pseudo-cursor +** will return NULL for every column. */ case OP_OpenPseudo: { VdbeCursor *pCx; @@ -97385,10 +97839,10 @@ case OP_ColumnsUsed: { ** ** See also: Found, NotFound, SeekGt, SeekGe, SeekLt */ -case OP_SeekLT: /* jump, in3, group, ncycle */ -case OP_SeekLE: /* jump, in3, group, ncycle */ -case OP_SeekGE: /* jump, in3, group, ncycle */ -case OP_SeekGT: { /* jump, in3, group, ncycle */ +case OP_SeekLT: /* jump0, in3, group, ncycle */ +case OP_SeekLE: /* jump0, in3, group, ncycle */ +case OP_SeekGE: /* jump0, in3, group, ncycle */ +case OP_SeekGT: { /* jump0, in3, group, ncycle */ int res; /* Comparison result */ int oc; /* Opcode */ VdbeCursor *pC; /* The cursor to seek */ @@ -98055,7 +98509,7 @@ case OP_Found: { /* jump, in3, ncycle */ ** ** See also: Found, NotFound, NoConflict, SeekRowid */ -case OP_SeekRowid: { /* jump, in3, ncycle */ +case OP_SeekRowid: { /* jump0, in3, ncycle */ VdbeCursor *pC; BtCursor *pCrsr; int res; @@ -98814,7 +99268,7 @@ case OP_NullRow: { ** configured to use Prev, not Next. */ case OP_SeekEnd: /* ncycle */ -case OP_Last: { /* jump, ncycle */ +case OP_Last: { /* jump0, ncycle */ VdbeCursor *pC; BtCursor *pCrsr; int res; @@ -98848,28 +99302,38 @@ case OP_Last: { /* jump, ncycle */ break; } -/* Opcode: IfSmaller P1 P2 P3 * * +/* Opcode: IfSizeBetween P1 P2 P3 P4 * ** -** Estimate the number of rows in the table P1. Jump to P2 if that -** estimate is less than approximately 2**(0.1*P3). +** Let N be the approximate number of rows in the table or index +** with cursor P1 and let X be 10*log2(N) if N is positive or -1 +** if N is zero. +** +** Jump to P2 if X is in between P3 and P4, inclusive. */ -case OP_IfSmaller: { /* jump */ +case OP_IfSizeBetween: { /* jump */ VdbeCursor *pC; BtCursor *pCrsr; int res; i64 sz; assert( pOp->p1>=0 && pOp->p1nCursor ); + assert( pOp->p4type==P4_INT32 ); + assert( pOp->p3>=-1 && pOp->p3<=640*2 ); + assert( pOp->p4.i>=-1 && pOp->p4.i<=640*2 ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); pCrsr = pC->uc.pCursor; assert( pCrsr ); rc = sqlite3BtreeFirst(pCrsr, &res); if( rc ) goto abort_due_to_error; - if( res==0 ){ + if( res!=0 ){ + sz = -1; /* -Infinity encoding */ + }else{ sz = sqlite3BtreeRowCountEst(pCrsr); - if( ALWAYS(sz>=0) && sqlite3LogEst((u64)sz)p3 ) res = 1; + assert( sz>0 ); + sz = sqlite3LogEst((u64)sz); } + res = sz>=pOp->p3 && sz<=pOp->p4.i; VdbeBranchTaken(res!=0,2); if( res ) goto jump_to_p2; break; @@ -98922,7 +99386,7 @@ case OP_Sort: { /* jump ncycle */ ** from the beginning toward the end. In other words, the cursor is ** configured to use Next, not Prev. */ -case OP_Rewind: { /* jump, ncycle */ +case OP_Rewind: { /* jump0, ncycle */ VdbeCursor *pC; BtCursor *pCrsr; int res; @@ -99569,11 +100033,18 @@ case OP_CreateBtree: { /* out2 */ break; } -/* Opcode: SqlExec * * * P4 * +/* Opcode: SqlExec P1 P2 * 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. +** +** The P1 parameter is a bitmask of options: +** +** 0x0001 Disable Auth and Trace callbacks while the statements +** in P4 are running. +** +** 0x0002 Set db->nAnalysisLimit to P2 while the statements in +** P4 are running. +** */ case OP_SqlExec: { char *zErr; @@ -99581,6 +100052,7 @@ case OP_SqlExec: { sqlite3_xauth xAuth; #endif u8 mTrace; + int savedAnalysisLimit; sqlite3VdbeIncrWriteCounter(p, 0); db->nSqlExec++; @@ -99589,18 +100061,23 @@ case OP_SqlExec: { xAuth = db->xAuth; #endif mTrace = db->mTrace; - if( pOp->p1 ){ + savedAnalysisLimit = db->nAnalysisLimit; + if( pOp->p1 & 0x0001 ){ #ifndef SQLITE_OMIT_AUTHORIZATION db->xAuth = 0; #endif db->mTrace = 0; } + if( pOp->p1 & 0x0002 ){ + db->nAnalysisLimit = pOp->p2; + } rc = sqlite3_exec(db, pOp->p4.z, 0, 0, &zErr); db->nSqlExec--; #ifndef SQLITE_OMIT_AUTHORIZATION db->xAuth = xAuth; #endif db->mTrace = mTrace; + db->nAnalysisLimit = savedAnalysisLimit; if( zErr || rc ){ sqlite3VdbeError(p, "%s", zErr); sqlite3_free(zErr); @@ -99752,11 +100229,11 @@ case OP_DropTrigger: { /* Opcode: IntegrityCk P1 P2 P3 P4 P5 ** ** Do an analysis of the currently open database. Store in -** register P1 the text of an error message describing any problems. -** If no problems are found, store a NULL in register P1. +** register (P1+1) the text of an error message describing any problems. +** If no problems are found, store a NULL in register (P1+1). ** -** The register P3 contains one less than the maximum number of allowed errors. -** At most reg(P3) errors will be reported. +** The register (P1) contains one less than the maximum number of allowed +** errors. At most reg(P1) errors will be reported. ** In other words, the analysis stops as soon as reg(P1) errors are ** seen. Reg(P1) is updated with the number of errors remaining. ** @@ -99776,19 +100253,21 @@ case OP_IntegrityCk: { Mem *pnErr; /* Register keeping track of errors remaining */ assert( p->bIsReader ); + assert( pOp->p4type==P4_INTARRAY ); nRoot = pOp->p2; aRoot = pOp->p4.ai; assert( nRoot>0 ); + assert( aRoot!=0 ); assert( aRoot[0]==(Pgno)nRoot ); - assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) ); - pnErr = &aMem[pOp->p3]; + assert( pOp->p1>0 && (pOp->p1+1)<=(p->nMem+1 - p->nCursor) ); + pnErr = &aMem[pOp->p1]; assert( (pnErr->flags & MEM_Int)!=0 ); assert( (pnErr->flags & (MEM_Str|MEM_Blob))==0 ); - pIn1 = &aMem[pOp->p1]; + pIn1 = &aMem[pOp->p1+1]; assert( pOp->p5nDb ); assert( DbMaskTest(p->btreeMask, pOp->p5) ); - rc = sqlite3BtreeIntegrityCheck(db, db->aDb[pOp->p5].pBt, &aRoot[1], nRoot, - (int)pnErr->u.i+1, &nErr, &z); + rc = sqlite3BtreeIntegrityCheck(db, db->aDb[pOp->p5].pBt, &aRoot[1], + &aMem[pOp->p3], nRoot, (int)pnErr->u.i+1, &nErr, &z); sqlite3VdbeMemSetNull(pIn1); if( nErr==0 ){ assert( z==0 ); @@ -99915,7 +100394,9 @@ case OP_RowSetTest: { /* jump, in1, in3 */ ** P1 contains the address of the memory cell that contains the first memory ** cell in an array of values used as arguments to the sub-program. P2 ** contains the address to jump to if the sub-program throws an IGNORE -** exception using the RAISE() function. Register P3 contains the address +** exception using the RAISE() function. P2 might be zero, if there is +** no possibility that an IGNORE exception will be raised. +** Register P3 contains the address ** of a memory cell in this (the parent) VM that is used to allocate the ** memory required by the sub-vdbe at runtime. ** @@ -99923,7 +100404,7 @@ case OP_RowSetTest: { /* jump, in1, in3 */ ** ** If P5 is non-zero, then recursive program invocation is enabled. */ -case OP_Program: { /* jump */ +case OP_Program: { /* jump0 */ int nMem; /* Number of memory registers for sub-program */ int nByte; /* Bytes of runtime space required for sub-program */ Mem *pRt; /* Register to allocate runtime space */ @@ -101472,7 +101953,7 @@ case OP_Filter: { /* jump */ ** error is encountered. */ case OP_Trace: -case OP_Init: { /* jump */ +case OP_Init: { /* jump0 */ int i; #ifndef SQLITE_OMIT_TRACE char *zTrace; @@ -105373,10 +105854,10 @@ static int bytecodevtabColumn( #ifdef SQLITE_ENABLE_STMT_SCANSTATUS case 9: /* nexec */ - sqlite3_result_int(ctx, pOp->nExec); + sqlite3_result_int64(ctx, pOp->nExec); break; case 10: /* ncycle */ - sqlite3_result_int(ctx, pOp->nCycle); + sqlite3_result_int64(ctx, pOp->nCycle); break; #else case 9: /* nexec */ @@ -106520,7 +107001,7 @@ static int lookupName( Parse *pParse, /* The parsing context */ const char *zDb, /* Name of the database containing table, or NULL */ const char *zTab, /* Name of table containing column, or NULL */ - const char *zCol, /* Name of the column. */ + const Expr *pRight, /* Name of the column. */ NameContext *pNC, /* The name context used to resolve the name */ Expr *pExpr /* Make this EXPR node point to the selected column */ ){ @@ -106537,6 +107018,7 @@ static int lookupName( Table *pTab = 0; /* Table holding the row */ Column *pCol; /* A column of pTab */ ExprList *pFJMatch = 0; /* Matches for FULL JOIN .. USING */ + const char *zCol = pRight->u.zToken; assert( pNC ); /* the name context cannot be NULL. */ assert( zCol ); /* The Z in X.Y.Z cannot be NULL */ @@ -106768,7 +107250,8 @@ static int lookupName( if( pParse->bReturning ){ if( (pNC->ncFlags & NC_UBaseReg)!=0 && ALWAYS(zTab==0 - || sqlite3StrICmp(zTab,pParse->pTriggerTab->zName)==0) + || sqlite3StrICmp(zTab,pParse->pTriggerTab->zName)==0 + || isValidSchemaTableName(zTab, pParse->pTriggerTab, 0)) ){ pExpr->iTable = op!=TK_DELETE; pTab = pParse->pTriggerTab; @@ -106872,6 +107355,11 @@ static int lookupName( && ALWAYS(VisibleRowid(pMatch->pTab) || pMatch->fg.isNestedFrom) ){ cnt = cntTab; +#if SQLITE_ALLOW_ROWID_IN_VIEW+0==2 + if( pMatch->pTab!=0 && IsView(pMatch->pTab) ){ + eNewExprOp = TK_NULL; + } +#endif if( pMatch->fg.isNestedFrom==0 ) pExpr->iColumn = -1; pExpr->affExpr = SQLITE_AFF_INTEGER; } @@ -107025,6 +107513,10 @@ static int lookupName( sqlite3ErrorMsg(pParse, "%s: %s.%s.%s", zErr, zDb, zTab, zCol); }else if( zTab ){ sqlite3ErrorMsg(pParse, "%s: %s.%s", zErr, zTab, zCol); + }else if( cnt==0 && ExprHasProperty(pRight,EP_DblQuoted) ){ + sqlite3ErrorMsg(pParse, "%s: \"%s\" - should this be a" + " string literal in single-quotes?", + zErr, zCol); }else{ sqlite3ErrorMsg(pParse, "%s: %s", zErr, zCol); } @@ -107058,8 +107550,12 @@ static int lookupName( ** If a generated column is referenced, set bits for every column ** of the table. */ - if( pExpr->iColumn>=0 && cnt==1 && pMatch!=0 ){ - pMatch->colUsed |= sqlite3ExprColUsed(pExpr); + if( pMatch ){ + if( pExpr->iColumn>=0 ){ + pMatch->colUsed |= sqlite3ExprColUsed(pExpr); + }else{ + pMatch->fg.rowidUsed = 1; + } } pExpr->op = eNewExprOp; @@ -107302,7 +107798,6 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ */ case TK_ID: case TK_DOT: { - const char *zColumn; const char *zTable; const char *zDb; Expr *pRight; @@ -107311,7 +107806,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ zDb = 0; zTable = 0; assert( !ExprHasProperty(pExpr, EP_IntValue) ); - zColumn = pExpr->u.zToken; + pRight = pExpr; }else{ Expr *pLeft = pExpr->pLeft; testcase( pNC->ncFlags & NC_IdxExpr ); @@ -107330,14 +107825,13 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ } assert( ExprUseUToken(pLeft) && ExprUseUToken(pRight) ); zTable = pLeft->u.zToken; - zColumn = pRight->u.zToken; assert( ExprUseYTab(pExpr) ); if( IN_RENAME_OBJECT ){ sqlite3RenameTokenRemap(pParse, (void*)pExpr, (void*)pRight); sqlite3RenameTokenRemap(pParse, (void*)&pExpr->y.pTab, (void*)pLeft); } } - return lookupName(pParse, zDb, zTable, zColumn, pNC, pExpr); + return lookupName(pParse, zDb, zTable, pRight, pNC, pExpr); } /* Resolve function names @@ -107513,11 +108007,9 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ #endif } } -#ifndef SQLITE_OMIT_WINDOWFUNC - else if( ExprHasProperty(pExpr, EP_WinFunc) ){ + else if( ExprHasProperty(pExpr, EP_WinFunc) || pExpr->pLeft ){ is_agg = 1; } -#endif sqlite3WalkExprList(pWalker, pList); if( is_agg ){ if( pExpr->pLeft ){ @@ -107587,6 +108079,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ testcase( pNC->ncFlags & NC_PartIdx ); testcase( pNC->ncFlags & NC_IdxExpr ); testcase( pNC->ncFlags & NC_GenCol ); + assert( pExpr->x.pSelect ); if( pNC->ncFlags & NC_SelfRef ){ notValidImpl(pParse, pNC, "subqueries", pExpr, pExpr); }else{ @@ -107595,6 +108088,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ assert( pNC->nRef>=nRef ); if( nRef!=pNC->nRef ){ ExprSetProperty(pExpr, EP_VarSelect); + pExpr->x.pSelect->selFlags |= SF_Correlated; } pNC->ncFlags |= NC_Subquery; } @@ -108120,6 +108614,7 @@ static int resolveSelectStep(Walker *pWalker, Select *p){ if( pOuterNC ) pOuterNC->nNestedSelect++; for(i=0; ipSrc->nSrc; i++){ SrcItem *pItem = &p->pSrc->a[i]; + assert( pItem->zName!=0 || pItem->pSelect!=0 );/* Test of tag-20240424-1*/ if( pItem->pSelect && (pItem->pSelect->selFlags & SF_Resolved)==0 ){ int nRef = pOuterNC ? pOuterNC->nRef : 0; const char *zSavedContext = pParse->zAuthContext; @@ -109426,11 +109921,12 @@ SQLITE_PRIVATE void sqlite3ExprSetErrorOffset(Expr *pExpr, int iOfst){ ** appear to be quoted. If the quotes were of the form "..." (double-quotes) ** then the EP_DblQuoted flag is set on the expression node. ** -** Special case: If op==TK_INTEGER and pToken points to a string that -** can be translated into a 32-bit integer, then the token is not -** stored in u.zToken. Instead, the integer values is written -** into u.iValue and the EP_IntValue flag is set. No extra storage +** Special case (tag-20240227-a): If op==TK_INTEGER and pToken points to +** a string that can be translated into a 32-bit integer, then the token is +** not stored in u.zToken. Instead, the integer values is written +** into u.iValue and the EP_IntValue flag is set. No extra storage ** is allocated to hold the integer text and the dequote flag is ignored. +** See also tag-20240227-b. */ SQLITE_PRIVATE Expr *sqlite3ExprAlloc( sqlite3 *db, /* Handle for sqlite3DbMallocRawNN() */ @@ -109446,7 +109942,7 @@ SQLITE_PRIVATE Expr *sqlite3ExprAlloc( if( pToken ){ if( op!=TK_INTEGER || pToken->z==0 || sqlite3GetInt32(pToken->z, &iValue)==0 ){ - nExtra = pToken->n+1; + nExtra = pToken->n+1; /* tag-20240227-a */ assert( iValue>=0 ); } } @@ -109878,6 +110374,7 @@ SQLITE_PRIVATE void sqlite3ExprAssignVarNumber(Parse *pParse, Expr *pExpr, u32 n static SQLITE_NOINLINE void sqlite3ExprDeleteNN(sqlite3 *db, Expr *p){ assert( p!=0 ); assert( db!=0 ); +exprDeleteRestart: assert( !ExprUseUValue(p) || p->u.iValue>=0 ); assert( !ExprUseYWin(p) || !ExprUseYSub(p) ); assert( !ExprUseYWin(p) || p->y.pWin!=0 || db->mallocFailed ); @@ -109893,7 +110390,6 @@ static SQLITE_NOINLINE void sqlite3ExprDeleteNN(sqlite3 *db, Expr *p){ if( !ExprHasProperty(p, (EP_TokenOnly|EP_Leaf)) ){ /* The Expr.x union is never used at the same time as Expr.pRight */ assert( (ExprUseXList(p) && p->x.pList==0) || p->pRight==0 ); - if( p->pLeft && p->op!=TK_SELECT_COLUMN ) sqlite3ExprDeleteNN(db, p->pLeft); if( p->pRight ){ assert( !ExprHasProperty(p, EP_WinFunc) ); sqlite3ExprDeleteNN(db, p->pRight); @@ -109908,6 +110404,19 @@ static SQLITE_NOINLINE void sqlite3ExprDeleteNN(sqlite3 *db, Expr *p){ } #endif } + if( p->pLeft && p->op!=TK_SELECT_COLUMN ){ + Expr *pLeft = p->pLeft; + if( !ExprHasProperty(p, EP_Static) + && !ExprHasProperty(pLeft, EP_Static) + ){ + /* Avoid unnecessary recursion on unary operators */ + sqlite3DbNNFreeNN(db, p); + p = pLeft; + goto exprDeleteRestart; + }else{ + sqlite3ExprDeleteNN(db, pLeft); + } + } } if( !ExprHasProperty(p, EP_Static) ){ sqlite3DbNNFreeNN(db, p); @@ -109940,11 +110449,11 @@ SQLITE_PRIVATE void sqlite3ClearOnOrUsing(sqlite3 *db, OnOrUsing *p){ ** ** The pExpr might be deleted immediately on an OOM error. ** -** The deferred delete is (currently) implemented by adding the -** pExpr to the pParse->pConstExpr list with a register number of 0. +** Return 0 if the delete was successfully deferred. Return non-zero +** if the delete happened immediately because of an OOM. */ -SQLITE_PRIVATE void sqlite3ExprDeferredDelete(Parse *pParse, Expr *pExpr){ - sqlite3ParserAddCleanup(pParse, sqlite3ExprDeleteGeneric, pExpr); +SQLITE_PRIVATE int sqlite3ExprDeferredDelete(Parse *pParse, Expr *pExpr){ + return 0==sqlite3ParserAddCleanup(pParse, sqlite3ExprDeleteGeneric, pExpr); } /* Invoke sqlite3RenameExprUnmap() and sqlite3ExprDelete() on the @@ -110380,17 +110889,19 @@ SQLITE_PRIVATE SrcList *sqlite3SrcListDup(sqlite3 *db, const SrcList *p, int fla pNewItem->iCursor = pOldItem->iCursor; pNewItem->addrFillSub = pOldItem->addrFillSub; pNewItem->regReturn = pOldItem->regReturn; + pNewItem->regResult = pOldItem->regResult; if( pNewItem->fg.isIndexedBy ){ pNewItem->u1.zIndexedBy = sqlite3DbStrDup(db, pOldItem->u1.zIndexedBy); + }else if( pNewItem->fg.isTabFunc ){ + pNewItem->u1.pFuncArg = + sqlite3ExprListDup(db, pOldItem->u1.pFuncArg, flags); + }else{ + pNewItem->u1.nRow = pOldItem->u1.nRow; } pNewItem->u2 = pOldItem->u2; if( pNewItem->fg.isCte ){ pNewItem->u2.pCteUse->nUse++; } - if( pNewItem->fg.isTabFunc ){ - pNewItem->u1.pFuncArg = - sqlite3ExprListDup(db, pOldItem->u1.pFuncArg, flags); - } pTab = pNewItem->pTab = pOldItem->pTab; if( pTab ){ pTab->nTabRef++; @@ -110856,6 +111367,54 @@ SQLITE_PRIVATE Expr *sqlite3ExprSimplifiedAndOr(Expr *pExpr){ return pExpr; } +/* +** pExpr is a TK_FUNCTION node. Try to determine whether or not the +** function is a constant function. A function is constant if all of +** the following are true: +** +** (1) It is a scalar function (not an aggregate or window function) +** (2) It has either the SQLITE_FUNC_CONSTANT or SQLITE_FUNC_SLOCHNG +** property. +** (3) All of its arguments are constants +** +** This routine sets pWalker->eCode to 0 if pExpr is not a constant. +** It makes no changes to pWalker->eCode if pExpr is constant. In +** every case, it returns WRC_Abort. +** +** Called as a service subroutine from exprNodeIsConstant(). +*/ +static SQLITE_NOINLINE int exprNodeIsConstantFunction( + Walker *pWalker, + Expr *pExpr +){ + int n; /* Number of arguments */ + ExprList *pList; /* List of arguments */ + FuncDef *pDef; /* The function */ + sqlite3 *db; /* The database */ + + assert( pExpr->op==TK_FUNCTION ); + if( ExprHasProperty(pExpr, EP_TokenOnly) + || (pList = pExpr->x.pList)==0 + ){; + n = 0; + }else{ + n = pList->nExpr; + sqlite3WalkExprList(pWalker, pList); + if( pWalker->eCode==0 ) return WRC_Abort; + } + db = pWalker->pParse->db; + pDef = sqlite3FindFunction(db, pExpr->u.zToken, n, ENC(db), 0); + if( pDef==0 + || pDef->xFinalize!=0 + || (pDef->funcFlags & (SQLITE_FUNC_CONSTANT|SQLITE_FUNC_SLOCHNG))==0 + || ExprHasProperty(pExpr, EP_WinFunc) + ){ + pWalker->eCode = 0; + return WRC_Abort; + } + return WRC_Prune; +} + /* ** These routines are Walker callbacks used to check expressions to @@ -110884,6 +111443,7 @@ SQLITE_PRIVATE Expr *sqlite3ExprSimplifiedAndOr(Expr *pExpr){ ** malformed schema error. */ static int exprNodeIsConstant(Walker *pWalker, Expr *pExpr){ + assert( pWalker->eCode>0 ); /* If pWalker->eCode is 2 then any term of the expression that comes from ** the ON or USING clauses of an outer join disqualifies the expression @@ -110903,6 +111463,8 @@ static int exprNodeIsConstant(Walker *pWalker, Expr *pExpr){ ){ if( pWalker->eCode==5 ) ExprSetProperty(pExpr, EP_FromDDL); return WRC_Continue; + }else if( pWalker->pParse ){ + return exprNodeIsConstantFunction(pWalker, pExpr); }else{ pWalker->eCode = 0; return WRC_Abort; @@ -110931,9 +111493,11 @@ static int exprNodeIsConstant(Walker *pWalker, Expr *pExpr){ case TK_IF_NULL_ROW: case TK_REGISTER: case TK_DOT: + case TK_RAISE: testcase( pExpr->op==TK_REGISTER ); testcase( pExpr->op==TK_IF_NULL_ROW ); testcase( pExpr->op==TK_DOT ); + testcase( pExpr->op==TK_RAISE ); pWalker->eCode = 0; return WRC_Abort; case TK_VARIABLE: @@ -110955,15 +111519,15 @@ static int exprNodeIsConstant(Walker *pWalker, Expr *pExpr){ return WRC_Continue; } } -static int exprIsConst(Expr *p, int initFlag, int iCur){ +static int exprIsConst(Parse *pParse, Expr *p, int initFlag){ Walker w; w.eCode = initFlag; + w.pParse = pParse; w.xExprCallback = exprNodeIsConstant; w.xSelectCallback = sqlite3SelectWalkFail; #ifdef SQLITE_DEBUG w.xSelectCallback2 = sqlite3SelectWalkAssert2; #endif - w.u.iCur = iCur; sqlite3WalkExpr(&w, p); return w.eCode; } @@ -110975,9 +111539,15 @@ static int exprIsConst(Expr *p, int initFlag, int iCur){ ** For the purposes of this function, a double-quoted string (ex: "abc") ** is considered a variable but a single-quoted string (ex: 'abc') is ** a constant. +** +** The pParse parameter may be NULL. But if it is NULL, there is no way +** to determine if function calls are constant or not, and hence all +** function calls will be considered to be non-constant. If pParse is +** not NULL, then a function call might be constant, depending on the +** function and on its parameters. */ -SQLITE_PRIVATE int sqlite3ExprIsConstant(Expr *p){ - return exprIsConst(p, 1, 0); +SQLITE_PRIVATE int sqlite3ExprIsConstant(Parse *pParse, Expr *p){ + return exprIsConst(pParse, p, 1); } /* @@ -110993,8 +111563,24 @@ SQLITE_PRIVATE int sqlite3ExprIsConstant(Expr *p){ ** can be added to the pParse->pConstExpr list and evaluated once when ** the prepared statement starts up. See sqlite3ExprCodeRunJustOnce(). */ -SQLITE_PRIVATE int sqlite3ExprIsConstantNotJoin(Expr *p){ - return exprIsConst(p, 2, 0); +static int sqlite3ExprIsConstantNotJoin(Parse *pParse, Expr *p){ + return exprIsConst(pParse, p, 2); +} + +/* +** This routine examines sub-SELECT statements as an expression is being +** walked as part of sqlite3ExprIsTableConstant(). Sub-SELECTs are considered +** constant as long as they are uncorrelated - meaning that they do not +** contain any terms from outer contexts. +*/ +static int exprSelectWalkTableConstant(Walker *pWalker, Select *pSelect){ + assert( pSelect!=0 ); + assert( pWalker->eCode==3 || pWalker->eCode==0 ); + if( (pSelect->selFlags & SF_Correlated)!=0 ){ + pWalker->eCode = 0; + return WRC_Abort; + } + return WRC_Prune; } /* @@ -111002,9 +111588,26 @@ SQLITE_PRIVATE int sqlite3ExprIsConstantNotJoin(Expr *p){ ** for any single row of the table with cursor iCur. In other words, the ** expression must not refer to any non-deterministic function nor any ** table other than iCur. +** +** Consider uncorrelated subqueries to be constants if the bAllowSubq +** parameter is true. */ -SQLITE_PRIVATE int sqlite3ExprIsTableConstant(Expr *p, int iCur){ - return exprIsConst(p, 3, iCur); +static int sqlite3ExprIsTableConstant(Expr *p, int iCur, int bAllowSubq){ + Walker w; + w.eCode = 3; + w.pParse = 0; + w.xExprCallback = exprNodeIsConstant; + if( bAllowSubq ){ + w.xSelectCallback = exprSelectWalkTableConstant; + }else{ + w.xSelectCallback = sqlite3SelectWalkFail; +#ifdef SQLITE_DEBUG + w.xSelectCallback2 = sqlite3SelectWalkAssert2; +#endif + } + w.u.iCur = iCur; + sqlite3WalkExpr(&w, p); + return w.eCode; } /* @@ -111022,7 +111625,10 @@ SQLITE_PRIVATE int sqlite3ExprIsTableConstant(Expr *p, int iCur){ ** ** (1) pExpr cannot refer to any table other than pSrc->iCursor. ** -** (2) pExpr cannot use subqueries or non-deterministic functions. +** (2a) pExpr cannot use subqueries unless the bAllowSubq parameter is +** true and the subquery is non-correlated +** +** (2b) pExpr cannot use non-deterministic functions. ** ** (3) pSrc cannot be part of the left operand for a RIGHT JOIN. ** (Is there some way to relax this constraint?) @@ -111051,7 +111657,8 @@ SQLITE_PRIVATE int sqlite3ExprIsTableConstant(Expr *p, int iCur){ SQLITE_PRIVATE int sqlite3ExprIsSingleTableConstraint( Expr *pExpr, /* The constraint */ const SrcList *pSrcList, /* Complete FROM clause */ - int iSrc /* Which element of pSrcList to use */ + int iSrc, /* Which element of pSrcList to use */ + int bAllowSubq /* Allow non-correlated subqueries */ ){ const SrcItem *pSrc = &pSrcList->a[iSrc]; if( pSrc->fg.jointype & JT_LTORJ ){ @@ -111076,7 +111683,8 @@ SQLITE_PRIVATE int sqlite3ExprIsSingleTableConstraint( } } } - return sqlite3ExprIsTableConstant(pExpr, pSrc->iCursor); /* rules (1), (2) */ + /* Rules (1), (2a), and (2b) handled by the following: */ + return sqlite3ExprIsTableConstant(pExpr, pSrc->iCursor, bAllowSubq); } @@ -111161,7 +111769,7 @@ SQLITE_PRIVATE int sqlite3ExprIsConstantOrGroupBy(Parse *pParse, Expr *p, ExprLi */ SQLITE_PRIVATE int sqlite3ExprIsConstantOrFunction(Expr *p, u8 isInit){ assert( isInit==0 || isInit==1 ); - return exprIsConst(p, 4+isInit, 0); + return exprIsConst(0, p, 4+isInit); } #ifdef SQLITE_ENABLE_CURSOR_HINTS @@ -111409,13 +112017,13 @@ static void sqlite3SetHasNullFlag(Vdbe *v, int iCur, int regHasNull){ ** The argument is an IN operator with a list (not a subquery) on the ** right-hand side. Return TRUE if that list is constant. */ -static int sqlite3InRhsIsConstant(Expr *pIn){ +static int sqlite3InRhsIsConstant(Parse *pParse, Expr *pIn){ Expr *pLHS; int res; assert( !ExprHasProperty(pIn, EP_xIsSelect) ); pLHS = pIn->pLeft; pIn->pLeft = 0; - res = sqlite3ExprIsConstant(pIn); + res = sqlite3ExprIsConstant(pParse, pIn); pIn->pLeft = pLHS; return res; } @@ -111684,7 +112292,7 @@ SQLITE_PRIVATE int sqlite3FindInIndex( if( eType==0 && (inFlags & IN_INDEX_NOOP_OK) && ExprUseXList(pX) - && (!sqlite3InRhsIsConstant(pX) || pX->x.pList->nExpr<=2) + && (!sqlite3InRhsIsConstant(pParse,pX) || pX->x.pList->nExpr<=2) ){ pParse->nTab--; /* Back out the allocation of the unused cursor */ iTab = -1; /* Cursor is not allocated */ @@ -111967,7 +112575,7 @@ SQLITE_PRIVATE void sqlite3CodeRhsOfIN( ** this code only executes once. Because for a non-constant ** expression we need to rerun this code each time. */ - if( addrOnce && !sqlite3ExprIsConstant(pE2) ){ + if( addrOnce && !sqlite3ExprIsConstant(pParse, pE2) ){ sqlite3VdbeChangeToNoop(v, addrOnce-1); sqlite3VdbeChangeToNoop(v, addrOnce); ExprClearProperty(pExpr, EP_Subrtn); @@ -113131,12 +113739,6 @@ expr_code_doover: assert( pExpr->u.zToken!=0 ); assert( pExpr->u.zToken[0]!=0 ); sqlite3VdbeAddOp2(v, OP_Variable, pExpr->iColumn, target); - if( pExpr->u.zToken[1]!=0 ){ - const char *z = sqlite3VListNumToName(pParse->pVList, pExpr->iColumn); - assert( pExpr->u.zToken[0]=='?' || (z && !strcmp(pExpr->u.zToken, z)) ); - pParse->pVList[0] = 0; /* Indicate VList may no longer be enlarged */ - sqlite3VdbeAppendP4(v, (char*)z, P4_STATIC); - } return target; } case TK_REGISTER: { @@ -113310,7 +113912,9 @@ expr_code_doover: } #endif - if( ConstFactorOk(pParse) && sqlite3ExprIsConstantNotJoin(pExpr) ){ + if( ConstFactorOk(pParse) + && sqlite3ExprIsConstantNotJoin(pParse,pExpr) + ){ /* SQL functions can be expensive. So try to avoid running them ** multiple times if we know they always give the same result */ return sqlite3ExprCodeRunJustOnce(pParse, pExpr, -1); @@ -113341,7 +113945,7 @@ expr_code_doover: } for(i=0; ia[i].pExpr) ){ + if( i<32 && sqlite3ExprIsConstant(pParse, pFarg->a[i].pExpr) ){ testcase( i==31 ); constMask |= MASKBIT32(i); } @@ -113483,8 +114087,9 @@ expr_code_doover: if( !ExprHasProperty(pExpr, EP_Collate) ){ /* A TK_COLLATE Expr node without the EP_Collate tag is a so-called ** "SOFT-COLLATE" that is added to constraints that are pushed down - ** from outer queries into sub-queries by the push-down optimization. - ** Clear subtypes as subtypes may not cross a subquery boundary. + ** from outer queries into sub-queries by the WHERE-clause push-down + ** optimization. Clear subtypes as subtypes may not cross a subquery + ** boundary. */ assert( pExpr->pLeft ); sqlite3ExprCode(pParse, pExpr->pLeft, target); @@ -113808,7 +114413,7 @@ SQLITE_PRIVATE int sqlite3ExprCodeTemp(Parse *pParse, Expr *pExpr, int *pReg){ if( ConstFactorOk(pParse) && ALWAYS(pExpr!=0) && pExpr->op!=TK_REGISTER - && sqlite3ExprIsConstantNotJoin(pExpr) + && sqlite3ExprIsConstantNotJoin(pParse, pExpr) ){ *pReg = 0; r2 = sqlite3ExprCodeRunJustOnce(pParse, pExpr, -1); @@ -113872,7 +114477,7 @@ SQLITE_PRIVATE void sqlite3ExprCodeCopy(Parse *pParse, Expr *pExpr, int target){ ** might choose to code the expression at initialization time. */ SQLITE_PRIVATE void sqlite3ExprCodeFactorable(Parse *pParse, Expr *pExpr, int target){ - if( pParse->okConstFactor && sqlite3ExprIsConstantNotJoin(pExpr) ){ + if( pParse->okConstFactor && sqlite3ExprIsConstantNotJoin(pParse,pExpr) ){ sqlite3ExprCodeRunJustOnce(pParse, pExpr, target); }else{ sqlite3ExprCodeCopy(pParse, pExpr, target); @@ -113931,7 +114536,7 @@ SQLITE_PRIVATE int sqlite3ExprCodeExprList( sqlite3VdbeAddOp2(v, copyOp, j+srcReg-1, target+i); } }else if( (flags & SQLITE_ECEL_FACTOR)!=0 - && sqlite3ExprIsConstantNotJoin(pExpr) + && sqlite3ExprIsConstantNotJoin(pParse,pExpr) ){ sqlite3ExprCodeRunJustOnce(pParse, pExpr, target+i); }else{ @@ -115082,9 +115687,8 @@ static int agginfoPersistExprCb(Walker *pWalker, Expr *pExpr){ && pAggInfo->aCol[iAgg].pCExpr==pExpr ){ pExpr = sqlite3ExprDup(db, pExpr, 0); - if( pExpr ){ + if( pExpr && !sqlite3ExprDeferredDelete(pParse, pExpr) ){ pAggInfo->aCol[iAgg].pCExpr = pExpr; - sqlite3ExprDeferredDelete(pParse, pExpr); } } }else{ @@ -115093,9 +115697,8 @@ static int agginfoPersistExprCb(Walker *pWalker, Expr *pExpr){ && pAggInfo->aFunc[iAgg].pFExpr==pExpr ){ pExpr = sqlite3ExprDup(db, pExpr, 0); - if( pExpr ){ + if( pExpr && !sqlite3ExprDeferredDelete(pParse, pExpr) ){ pAggInfo->aFunc[iAgg].pFExpr = pExpr; - sqlite3ExprDeferredDelete(pParse, pExpr); } } } @@ -117796,7 +118399,12 @@ SQLITE_PRIVATE void sqlite3AlterDropColumn(Parse *pParse, SrcList *pSrc, const T if( i==pTab->iPKey ){ sqlite3VdbeAddOp2(v, OP_Null, 0, regOut); }else{ + char aff = pTab->aCol[i].affinity; + if( aff==SQLITE_AFF_REAL ){ + pTab->aCol[i].affinity = SQLITE_AFF_NUMERIC; + } sqlite3ExprCodeGetColumnOfTable(v, pTab, iCur, i, regOut); + pTab->aCol[i].affinity = aff; } nField++; } @@ -118715,7 +119323,7 @@ static void statGet( 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] ); + assert( p->current.anEq[i] || p->nRow==0 ); #endif } sqlite3ResultStrAccum(context, &sStat); @@ -118900,7 +119508,7 @@ static void analyzeOneTable( for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ int nCol; /* Number of columns in pIdx. "N" */ - int addrRewind; /* Address of "OP_Rewind iIdxCur" */ + int addrGotoEnd; /* Address of "OP_Rewind iIdxCur" */ int addrNextRow; /* Address of "next_row:" */ const char *zIdxName; /* Name of the index */ int nColTest; /* Number of columns to test for changes */ @@ -118924,9 +119532,14 @@ static void analyzeOneTable( /* ** Pseudo-code for loop that calls stat_push(): ** - ** Rewind csr - ** if eof(csr) goto end_of_scan; ** regChng = 0 + ** Rewind csr + ** if eof(csr){ + ** stat_init() with count = 0; + ** goto end_of_scan; + ** } + ** count() + ** stat_init() ** goto chng_addr_0; ** ** next_row: @@ -118965,41 +119578,36 @@ static void analyzeOneTable( sqlite3VdbeSetP4KeyInfo(pParse, pIdx); VdbeComment((v, "%s", pIdx->zName)); - /* Invoke the stat_init() function. The arguments are: + /* Implementation of the following: ** + ** regChng = 0 + ** Rewind csr + ** if eof(csr){ + ** stat_init() with count = 0; + ** goto end_of_scan; + ** } + ** count() + ** stat_init() + ** goto chng_addr_0; + */ + assert( regTemp2==regStat+4 ); + sqlite3VdbeAddOp2(v, OP_Integer, db->nAnalysisLimit, regTemp2); + + /* Arguments to stat_init(): ** (1) the number of columns in the index including the rowid ** (or for a WITHOUT ROWID table, the number of PK columns), ** (2) the number of columns in the key without the rowid/pk - ** (3) estimated number of rows in the index, - */ + ** (3) estimated number of rows in the index. */ sqlite3VdbeAddOp2(v, OP_Integer, nCol, regStat+1); assert( regRowid==regStat+2 ); sqlite3VdbeAddOp2(v, OP_Integer, pIdx->nKeyCol, regRowid); -#ifdef SQLITE_ENABLE_STAT4 - if( OptimizationEnabled(db, SQLITE_Stat4) ){ - sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regTemp); - addrRewind = sqlite3VdbeAddOp1(v, OP_Rewind, iIdxCur); - VdbeCoverage(v); - }else -#endif - { - addrRewind = sqlite3VdbeAddOp1(v, OP_Rewind, iIdxCur); - VdbeCoverage(v); - sqlite3VdbeAddOp3(v, OP_Count, iIdxCur, regTemp, 1); - } - assert( regTemp2==regStat+4 ); - sqlite3VdbeAddOp2(v, OP_Integer, db->nAnalysisLimit, regTemp2); + sqlite3VdbeAddOp3(v, OP_Count, iIdxCur, regTemp, + OptimizationDisabled(db, SQLITE_Stat4)); sqlite3VdbeAddFunctionCall(pParse, 0, regStat+1, regStat, 4, &statInitFuncdef, 0); + addrGotoEnd = sqlite3VdbeAddOp1(v, OP_Rewind, iIdxCur); + VdbeCoverage(v); - /* Implementation of the following: - ** - ** Rewind csr - ** if eof(csr) goto end_of_scan; - ** regChng = 0 - ** goto next_push_0; - ** - */ sqlite3VdbeAddOp2(v, OP_Integer, 0, regChng); addrNextRow = sqlite3VdbeCurrentAddr(v); @@ -119106,6 +119714,12 @@ static void analyzeOneTable( } /* Add the entry to the stat1 table. */ + if( pIdx->pPartIdxWhere ){ + /* Partial indexes might get a zero-entry in sqlite_stat1. But + ** an empty table is omitted from sqlite_stat1. */ + sqlite3VdbeJumpHere(v, addrGotoEnd); + addrGotoEnd = 0; + } callStatGet(pParse, regStat, STAT_GET_STAT1, regStat1); assert( "BBB"[0]==SQLITE_AFF_TEXT ); sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 3, regTemp, "BBB", 0); @@ -119129,6 +119743,13 @@ static void analyzeOneTable( int addrIsNull; u8 seekOp = HasRowid(pTab) ? OP_NotExists : OP_NotFound; + /* No STAT4 data is generated if the number of rows is zero */ + if( addrGotoEnd==0 ){ + sqlite3VdbeAddOp2(v, OP_Cast, regStat1, SQLITE_AFF_INTEGER); + addrGotoEnd = sqlite3VdbeAddOp1(v, OP_IfNot, regStat1); + VdbeCoverage(v); + } + if( doOnce ){ int mxCol = nCol; Index *pX; @@ -119181,7 +119802,7 @@ static void analyzeOneTable( #endif /* SQLITE_ENABLE_STAT4 */ /* End of analysis */ - sqlite3VdbeJumpHere(v, addrRewind); + if( addrGotoEnd ) sqlite3VdbeJumpHere(v, addrGotoEnd); } @@ -120930,7 +121551,7 @@ SQLITE_PRIVATE void sqlite3FinishCoding(Parse *pParse){ } sqlite3VdbeAddOp0(v, OP_Halt); -#if SQLITE_USER_AUTHENTICATION +#if SQLITE_USER_AUTHENTICATION && !defined(SQLITE_OMIT_SHARED_CACHE) if( pParse->nTableLock>0 && db->init.busy==0 ){ sqlite3UserAuthInit(db); if( db->auth.authLevelrc = SQLITE_ERROR; pParse->nErr++; return; } + iCsr = pParse->nTab++; regYield = ++pParse->nMem; regRec = ++pParse->nMem; regRowid = ++pParse->nMem; - assert(pParse->nTab==1); sqlite3MayAbort(pParse); - sqlite3VdbeAddOp3(v, OP_OpenWrite, 1, pParse->regRoot, iDb); + sqlite3VdbeAddOp3(v, OP_OpenWrite, iCsr, pParse->regRoot, iDb); sqlite3VdbeChangeP5(v, OPFLAG_P2ISREG); - pParse->nTab = 2; addrTop = sqlite3VdbeCurrentAddr(v) + 1; sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, addrTop); if( pParse->nErr ) return; @@ -123603,11 +124224,11 @@ SQLITE_PRIVATE void sqlite3EndTable( VdbeCoverage(v); sqlite3VdbeAddOp3(v, OP_MakeRecord, dest.iSdst, dest.nSdst, regRec); sqlite3TableAffinity(v, p, 0); - sqlite3VdbeAddOp2(v, OP_NewRowid, 1, regRowid); - sqlite3VdbeAddOp3(v, OP_Insert, 1, regRec, regRowid); + sqlite3VdbeAddOp2(v, OP_NewRowid, iCsr, regRowid); + sqlite3VdbeAddOp3(v, OP_Insert, iCsr, regRec, regRowid); sqlite3VdbeGoto(v, addrInsLoop); sqlite3VdbeJumpHere(v, addrInsLoop); - sqlite3VdbeAddOp1(v, OP_Close, 1); + sqlite3VdbeAddOp1(v, OP_Close, iCsr); } /* Compute the complete text of the CREATE statement */ @@ -123664,13 +124285,10 @@ SQLITE_PRIVATE void sqlite3EndTable( /* 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, + sqlite3VdbeAddOp4(v, OP_SqlExec, 0x0001, 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. @@ -132844,6 +133462,195 @@ SQLITE_PRIVATE void sqlite3AutoincrementEnd(Parse *pParse){ # define autoIncStep(A,B,C) #endif /* SQLITE_OMIT_AUTOINCREMENT */ +/* +** If argument pVal is a Select object returned by an sqlite3MultiValues() +** that was able to use the co-routine optimization, finish coding the +** co-routine. +*/ +SQLITE_PRIVATE void sqlite3MultiValuesEnd(Parse *pParse, Select *pVal){ + if( ALWAYS(pVal) && pVal->pSrc->nSrc>0 ){ + SrcItem *pItem = &pVal->pSrc->a[0]; + sqlite3VdbeEndCoroutine(pParse->pVdbe, pItem->regReturn); + sqlite3VdbeJumpHere(pParse->pVdbe, pItem->addrFillSub - 1); + } +} + +/* +** Return true if all expressions in the expression-list passed as the +** only argument are constant. +*/ +static int exprListIsConstant(Parse *pParse, ExprList *pRow){ + int ii; + for(ii=0; iinExpr; ii++){ + if( 0==sqlite3ExprIsConstant(pParse, pRow->a[ii].pExpr) ) return 0; + } + return 1; +} + +/* +** Return true if all expressions in the expression-list passed as the +** only argument are both constant and have no affinity. +*/ +static int exprListIsNoAffinity(Parse *pParse, ExprList *pRow){ + int ii; + if( exprListIsConstant(pParse,pRow)==0 ) return 0; + for(ii=0; iinExpr; ii++){ + Expr *pExpr = pRow->a[ii].pExpr; + assert( pExpr->op!=TK_RAISE ); + assert( pExpr->affExpr==0 ); + if( 0!=sqlite3ExprAffinity(pExpr) ) return 0; + } + return 1; + +} + +/* +** This function is called by the parser for the second and subsequent +** rows of a multi-row VALUES clause. Argument pLeft is the part of +** the VALUES clause already parsed, argument pRow is the vector of values +** for the new row. The Select object returned represents the complete +** VALUES clause, including the new row. +** +** There are two ways in which this may be achieved - by incremental +** coding of a co-routine (the "co-routine" method) or by returning a +** Select object equivalent to the following (the "UNION ALL" method): +** +** "pLeft UNION ALL SELECT pRow" +** +** If the VALUES clause contains a lot of rows, this compound Select +** object may consume a lot of memory. +** +** When the co-routine method is used, each row that will be returned +** by the VALUES clause is coded into part of a co-routine as it is +** passed to this function. The returned Select object is equivalent to: +** +** SELECT * FROM ( +** Select object to read co-routine +** ) +** +** The co-routine method is used in most cases. Exceptions are: +** +** a) If the current statement has a WITH clause. This is to avoid +** statements like: +** +** WITH cte AS ( VALUES('x'), ('y') ... ) +** SELECT * FROM cte AS a, cte AS b; +** +** This will not work, as the co-routine uses a hard-coded register +** for its OP_Yield instructions, and so it is not possible for two +** cursors to iterate through it concurrently. +** +** b) The schema is currently being parsed (i.e. the VALUES clause is part +** of a schema item like a VIEW or TRIGGER). In this case there is no VM +** being generated when parsing is taking place, and so generating +** a co-routine is not possible. +** +** c) There are non-constant expressions in the VALUES clause (e.g. +** the VALUES clause is part of a correlated sub-query). +** +** d) One or more of the values in the first row of the VALUES clause +** has an affinity (i.e. is a CAST expression). This causes problems +** because the complex rules SQLite uses (see function +** sqlite3SubqueryColumnTypes() in select.c) to determine the effective +** affinity of such a column for all rows require access to all values in +** the column simultaneously. +*/ +SQLITE_PRIVATE Select *sqlite3MultiValues(Parse *pParse, Select *pLeft, ExprList *pRow){ + + if( pParse->bHasWith /* condition (a) above */ + || pParse->db->init.busy /* condition (b) above */ + || exprListIsConstant(pParse,pRow)==0 /* condition (c) above */ + || (pLeft->pSrc->nSrc==0 && + exprListIsNoAffinity(pParse,pLeft->pEList)==0) /* condition (d) above */ + || IN_SPECIAL_PARSE + ){ + /* The co-routine method cannot be used. Fall back to UNION ALL. */ + Select *pSelect = 0; + int f = SF_Values | SF_MultiValue; + if( pLeft->pSrc->nSrc ){ + sqlite3MultiValuesEnd(pParse, pLeft); + f = SF_Values; + }else if( pLeft->pPrior ){ + /* In this case set the SF_MultiValue flag only if it was set on pLeft */ + f = (f & pLeft->selFlags); + } + pSelect = sqlite3SelectNew(pParse, pRow, 0, 0, 0, 0, 0, f, 0); + pLeft->selFlags &= ~SF_MultiValue; + if( pSelect ){ + pSelect->op = TK_ALL; + pSelect->pPrior = pLeft; + pLeft = pSelect; + } + }else{ + SrcItem *p = 0; /* SrcItem that reads from co-routine */ + + if( pLeft->pSrc->nSrc==0 ){ + /* Co-routine has not yet been started and the special Select object + ** that accesses the co-routine has not yet been created. This block + ** does both those things. */ + Vdbe *v = sqlite3GetVdbe(pParse); + Select *pRet = sqlite3SelectNew(pParse, 0, 0, 0, 0, 0, 0, 0, 0); + + /* Ensure the database schema has been read. This is to ensure we have + ** the correct text encoding. */ + if( (pParse->db->mDbFlags & DBFLAG_SchemaKnownOk)==0 ){ + sqlite3ReadSchema(pParse); + } + + if( pRet ){ + SelectDest dest; + pRet->pSrc->nSrc = 1; + pRet->pPrior = pLeft->pPrior; + pRet->op = pLeft->op; + pLeft->pPrior = 0; + pLeft->op = TK_SELECT; + assert( pLeft->pNext==0 ); + assert( pRet->pNext==0 ); + p = &pRet->pSrc->a[0]; + p->pSelect = pLeft; + p->fg.viaCoroutine = 1; + p->addrFillSub = sqlite3VdbeCurrentAddr(v) + 1; + p->regReturn = ++pParse->nMem; + p->iCursor = -1; + p->u1.nRow = 2; + sqlite3VdbeAddOp3(v,OP_InitCoroutine,p->regReturn,0,p->addrFillSub); + sqlite3SelectDestInit(&dest, SRT_Coroutine, p->regReturn); + + /* Allocate registers for the output of the co-routine. Do so so + ** that there are two unused registers immediately before those + ** used by the co-routine. This allows the code in sqlite3Insert() + ** to use these registers directly, instead of copying the output + ** of the co-routine to a separate array for processing. */ + dest.iSdst = pParse->nMem + 3; + dest.nSdst = pLeft->pEList->nExpr; + pParse->nMem += 2 + dest.nSdst; + + pLeft->selFlags |= SF_MultiValue; + sqlite3Select(pParse, pLeft, &dest); + p->regResult = dest.iSdst; + assert( pParse->nErr || dest.iSdst>0 ); + pLeft = pRet; + } + }else{ + p = &pLeft->pSrc->a[0]; + assert( !p->fg.isTabFunc && !p->fg.isIndexedBy ); + p->u1.nRow++; + } + + if( pParse->nErr==0 ){ + assert( p!=0 ); + if( p->pSelect->pEList->nExpr!=pRow->nExpr ){ + sqlite3SelectWrongNumTermsError(pParse, p->pSelect); + }else{ + sqlite3ExprCodeExprList(pParse, pRow, p->regResult, 0, 0); + sqlite3VdbeAddOp1(pParse->pVdbe, OP_Yield, p->regReturn); + } + } + sqlite3ExprListDelete(pParse->db, pRow); + } + + return pLeft; +} /* Forward declaration */ static int xferOptimization( @@ -133180,25 +133987,40 @@ SQLITE_PRIVATE void sqlite3Insert( if( pSelect ){ /* Data is coming from a SELECT or from a multi-row VALUES clause. ** Generate a co-routine to run the SELECT. */ - int regYield; /* Register holding co-routine entry-point */ - int addrTop; /* Top of the co-routine */ int rc; /* Result code */ - regYield = ++pParse->nMem; - addrTop = sqlite3VdbeCurrentAddr(v) + 1; - sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, addrTop); - sqlite3SelectDestInit(&dest, SRT_Coroutine, regYield); - dest.iSdst = bIdListInOrder ? regData : 0; - dest.nSdst = pTab->nCol; - rc = sqlite3Select(pParse, pSelect, &dest); - regFromSelect = dest.iSdst; - assert( db->pParse==pParse ); - if( rc || pParse->nErr ) goto insert_cleanup; - assert( db->mallocFailed==0 ); - sqlite3VdbeEndCoroutine(v, regYield); - sqlite3VdbeJumpHere(v, addrTop - 1); /* label B: */ - assert( pSelect->pEList ); - nColumn = pSelect->pEList->nExpr; + if( pSelect->pSrc->nSrc==1 + && pSelect->pSrc->a[0].fg.viaCoroutine + && pSelect->pPrior==0 + ){ + SrcItem *pItem = &pSelect->pSrc->a[0]; + dest.iSDParm = pItem->regReturn; + regFromSelect = pItem->regResult; + nColumn = pItem->pSelect->pEList->nExpr; + ExplainQueryPlan((pParse, 0, "SCAN %S", pItem)); + if( bIdListInOrder && nColumn==pTab->nCol ){ + regData = regFromSelect; + regRowid = regData - 1; + regIns = regRowid - (IsVirtual(pTab) ? 1 : 0); + } + }else{ + int addrTop; /* Top of the co-routine */ + int regYield = ++pParse->nMem; + addrTop = sqlite3VdbeCurrentAddr(v) + 1; + sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, addrTop); + sqlite3SelectDestInit(&dest, SRT_Coroutine, regYield); + dest.iSdst = bIdListInOrder ? regData : 0; + dest.nSdst = pTab->nCol; + rc = sqlite3Select(pParse, pSelect, &dest); + regFromSelect = dest.iSdst; + assert( db->pParse==pParse ); + if( rc || pParse->nErr ) goto insert_cleanup; + assert( db->mallocFailed==0 ); + sqlite3VdbeEndCoroutine(v, regYield); + sqlite3VdbeJumpHere(v, addrTop - 1); /* label B: */ + assert( pSelect->pEList ); + nColumn = pSelect->pEList->nExpr; + } /* Set useTempTable to TRUE if the result of the SELECT statement ** should be written into a temporary table (template 4). Set to @@ -137923,6 +138745,34 @@ static const PragmaName aPragmaName[] = { /************** End of pragma.h **********************************************/ /************** Continuing where we left off in pragma.c *********************/ +/* +** When the 0x10 bit of PRAGMA optimize is set, any ANALYZE commands +** will be run with an analysis_limit set to the lessor of the value of +** the following macro or to the actual analysis_limit if it is non-zero, +** in order to prevent PRAGMA optimize from running for too long. +** +** The value of 2000 is chosen emperically so that the worst-case run-time +** for PRAGMA optimize does not exceed 100 milliseconds against a variety +** of test databases on a RaspberryPI-4 compiled using -Os and without +** -DSQLITE_DEBUG. Of course, your mileage may vary. For the purpose of +** this paragraph, "worst-case" means that ANALYZE ends up being +** run on every table in the database. The worst case typically only +** happens if PRAGMA optimize is run on a database file for which ANALYZE +** has not been previously run and the 0x10000 flag is included so that +** all tables are analyzed. The usual case for PRAGMA optimize is that +** no ANALYZE commands will be run at all, or if any ANALYZE happens it +** will be against a single table, so that expected timing for PRAGMA +** optimize on a PI-4 is more like 1 millisecond or less with the 0x10000 +** flag or less than 100 microseconds without the 0x10000 flag. +** +** An analysis limit of 2000 is almost always sufficient for the query +** planner to fully characterize an index. The additional accuracy from +** a larger analysis is not usually helpful. +*/ +#ifndef SQLITE_DEFAULT_OPTIMIZE_LIMIT +# define SQLITE_DEFAULT_OPTIMIZE_LIMIT 2000 +#endif + /* ** Interpret the given string as a safety level. Return 0 for OFF, ** 1 for ON or NORMAL, 2 for FULL, and 3 for EXTRA. Return 1 for an empty or @@ -139568,7 +140418,7 @@ SQLITE_PRIVATE void sqlite3Pragma( /* Set the maximum error count */ mxErr = SQLITE_INTEGRITY_CHECK_ERROR_MAX; if( zRight ){ - if( sqlite3GetInt32(zRight, &mxErr) ){ + if( sqlite3GetInt32(pValue->z, &mxErr) ){ if( mxErr<=0 ){ mxErr = SQLITE_INTEGRITY_CHECK_ERROR_MAX; } @@ -139585,7 +140435,6 @@ SQLITE_PRIVATE void sqlite3Pragma( Hash *pTbls; /* Set of all tables in the schema */ int *aRoot; /* Array of root page numbers of all btrees */ int cnt = 0; /* Number of entries in aRoot[] */ - int mxIdx = 0; /* Maximum number of indexes for any table */ if( OMIT_TEMPDB && i==1 ) continue; if( iDb>=0 && i!=iDb ) continue; @@ -139607,7 +140456,6 @@ SQLITE_PRIVATE void sqlite3Pragma( if( pObjTab && pObjTab!=pTab ) continue; if( HasRowid(pTab) ) cnt++; for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){ cnt++; } - if( nIdx>mxIdx ) mxIdx = nIdx; } if( cnt==0 ) continue; if( pObjTab ) cnt++; @@ -139627,11 +140475,11 @@ SQLITE_PRIVATE void sqlite3Pragma( aRoot[0] = cnt; /* Make sure sufficient number of registers have been allocated */ - sqlite3TouchRegister(pParse, 8+mxIdx); + sqlite3TouchRegister(pParse, 8+cnt); sqlite3ClearTempRegCache(pParse); /* Do the b-tree integrity checks */ - sqlite3VdbeAddOp4(v, OP_IntegrityCk, 2, cnt, 1, (char*)aRoot,P4_INTARRAY); + sqlite3VdbeAddOp4(v, OP_IntegrityCk, 1, cnt, 8, (char*)aRoot,P4_INTARRAY); sqlite3VdbeChangeP5(v, (u8)i); addr = sqlite3VdbeAddOp1(v, OP_IsNull, 2); VdbeCoverage(v); sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, @@ -139641,6 +140489,36 @@ SQLITE_PRIVATE void sqlite3Pragma( integrityCheckResultRow(v); sqlite3VdbeJumpHere(v, addr); + /* Check that the indexes all have the right number of rows */ + cnt = pObjTab ? 1 : 0; + sqlite3VdbeLoadString(v, 2, "wrong # of entries in index "); + for(x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){ + int iTab = 0; + Table *pTab = sqliteHashData(x); + Index *pIdx; + if( pObjTab && pObjTab!=pTab ) continue; + if( HasRowid(pTab) ){ + iTab = cnt++; + }else{ + iTab = cnt; + for(pIdx=pTab->pIndex; ALWAYS(pIdx); pIdx=pIdx->pNext){ + if( IsPrimaryKeyIndex(pIdx) ) break; + iTab++; + } + } + for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ + if( pIdx->pPartIdxWhere==0 ){ + addr = sqlite3VdbeAddOp3(v, OP_Eq, 8+cnt, 0, 8+iTab); + VdbeCoverageNeverNull(v); + sqlite3VdbeLoadString(v, 4, pIdx->zName); + sqlite3VdbeAddOp3(v, OP_Concat, 4, 2, 3); + integrityCheckResultRow(v); + sqlite3VdbeJumpHere(v, addr); + } + cnt++; + } + } + /* Make sure all the indices are constructed correctly. */ for(x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){ @@ -139964,21 +140842,9 @@ SQLITE_PRIVATE void sqlite3Pragma( } sqlite3VdbeAddOp2(v, OP_Next, iDataCur, loopTop); VdbeCoverage(v); sqlite3VdbeJumpHere(v, loopTop-1); - if( !isQuick ){ - sqlite3VdbeLoadString(v, 2, "wrong # of entries in index "); - for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){ - if( pPk==pIdx ) continue; - sqlite3VdbeAddOp2(v, OP_Count, iIdxCur+j, 3); - addr = sqlite3VdbeAddOp3(v, OP_Eq, 8+j, 0, 3); VdbeCoverage(v); - sqlite3VdbeChangeP5(v, SQLITE_NOTNULL); - sqlite3VdbeLoadString(v, 4, pIdx->zName); - sqlite3VdbeAddOp3(v, OP_Concat, 4, 2, 3); - integrityCheckResultRow(v); - sqlite3VdbeJumpHere(v, addr); - } - if( pPk ){ - sqlite3ReleaseTempRange(pParse, r2, pPk->nKeyCol); - } + if( pPk ){ + assert( !isQuick ); + sqlite3ReleaseTempRange(pParse, r2, pPk->nKeyCol); } } @@ -140276,44 +141142,63 @@ SQLITE_PRIVATE void sqlite3Pragma( ** ** The optional argument is a bitmask of optimizations to perform: ** - ** 0x0001 Debugging mode. Do not actually perform any optimizations - ** but instead return one line of text for each optimization - ** that would have been done. Off by default. + ** 0x00001 Debugging mode. Do not actually perform any optimizations + ** but instead return one line of text for each optimization + ** that would have been done. Off by default. ** - ** 0x0002 Run ANALYZE on tables that might benefit. On by default. - ** See below for additional information. + ** 0x00002 Run ANALYZE on tables that might benefit. On by default. + ** See below for additional information. ** - ** 0x0004 (Not yet implemented) Record usage and performance - ** information from the current session in the - ** database file so that it will be available to "optimize" - ** pragmas run by future database connections. + ** 0x00010 Run all ANALYZE operations using an analysis_limit that + ** is the lessor of the current analysis_limit and the + ** SQLITE_DEFAULT_OPTIMIZE_LIMIT compile-time option. + ** The default value of SQLITE_DEFAULT_OPTIMIZE_LIMIT is + ** currently (2024-02-19) set to 2000, which is such that + ** the worst case run-time for PRAGMA optimize on a 100MB + ** database will usually be less than 100 milliseconds on + ** a RaspberryPI-4 class machine. On by default. ** - ** 0x0008 (Not yet implemented) Create indexes that might have - ** been helpful to recent queries + ** 0x10000 Look at tables to see if they need to be reanalyzed + ** due to growth or shrinkage even if they have not been + ** queried during the current connection. Off by default. ** - ** The default MASK is and always shall be 0xfffe. 0xfffe means perform all - ** of the optimizations listed above except Debug Mode, including new - ** optimizations that have not yet been invented. If new optimizations are - ** ever added that should be off by default, those off-by-default - ** optimizations will have bitmasks of 0x10000 or larger. + ** The default MASK is and always shall be 0x0fffe. In the current + ** implementation, the default mask only covers the 0x00002 optimization, + ** though additional optimizations that are covered by 0x0fffe might be + ** added in the future. Optimizations that are off by default and must + ** be explicitly requested have masks of 0x10000 or greater. ** ** DETERMINATION OF WHEN TO RUN ANALYZE ** ** In the current implementation, a table is analyzed if only if all of ** the following are true: ** - ** (1) MASK bit 0x02 is set. + ** (1) MASK bit 0x00002 is set. ** - ** (2) The query planner used sqlite_stat1-style statistics for one or - ** more indexes of the table at some point during the lifetime of - ** the current connection. + ** (2) The table is an ordinary table, not a virtual table or view. ** - ** (3) One or more indexes of the table are currently unanalyzed OR - ** the number of rows in the table has increased by 25 times or more - ** since the last time ANALYZE was run. + ** (3) The table name does not begin with "sqlite_". + ** + ** (4) One or more of the following is true: + ** (4a) The 0x10000 MASK bit is set. + ** (4b) One or more indexes on the table lacks an entry + ** in the sqlite_stat1 table. + ** (4c) The query planner used sqlite_stat1-style statistics for one + ** or more indexes of the table at some point during the lifetime + ** of the current connection. + ** + ** (5) One or more of the following is true: + ** (5a) One or more indexes on the table lacks an entry + ** in the sqlite_stat1 table. (Same as 4a) + ** (5b) The number of rows in the table has increased or decreased by + ** 10-fold. In other words, the current size of the table is + ** 10 times larger than the size in sqlite_stat1 or else the + ** current size is less than 1/10th the size in sqlite_stat1. ** ** The rules for when tables are analyzed are likely to change in - ** future releases. + ** future releases. Future versions of SQLite might accept a string + ** literal argument to this pragma that contains a mnemonic description + ** of the options rather than a bitmap. */ case PragTyp_OPTIMIZE: { int iDbLast; /* Loop termination point for the schema loop */ @@ -140325,6 +141210,10 @@ SQLITE_PRIVATE void sqlite3Pragma( LogEst szThreshold; /* Size threshold above which reanalysis needed */ char *zSubSql; /* SQL statement for the OP_SqlExec opcode */ u32 opMask; /* Mask of operations to perform */ + int nLimit; /* Analysis limit to use */ + int nCheck = 0; /* Number of tables to be optimized */ + int nBtree = 0; /* Number of btrees to scan */ + int nIndex; /* Number of indexes on the current table */ if( zRight ){ opMask = (u32)sqlite3Atoi(zRight); @@ -140332,6 +141221,14 @@ SQLITE_PRIVATE void sqlite3Pragma( }else{ opMask = 0xfffe; } + if( (opMask & 0x10)==0 ){ + nLimit = 0; + }else if( db->nAnalysisLimit>0 + && db->nAnalysisLimitnTab++; for(iDbLast = zDb?iDb:db->nDb-1; iDb<=iDbLast; iDb++){ if( iDb==1 ) continue; @@ -140340,23 +141237,61 @@ SQLITE_PRIVATE void sqlite3Pragma( for(k=sqliteHashFirst(&pSchema->tblHash); k; k=sqliteHashNext(k)){ pTab = (Table*)sqliteHashData(k); - /* If table pTab has not been used in a way that would benefit from - ** having analysis statistics during the current session, then skip it. - ** This also has the effect of skipping virtual tables and views */ - if( (pTab->tabFlags & TF_StatsUsed)==0 ) continue; + /* This only works for ordinary tables */ + if( !IsOrdinaryTable(pTab) ) continue; - /* Reanalyze if the table is 25 times larger than the last analysis */ - szThreshold = pTab->nRowLogEst + 46; assert( sqlite3LogEst(25)==46 ); + /* Do not scan system tables */ + if( 0==sqlite3StrNICmp(pTab->zName, "sqlite_", 7) ) continue; + + /* Find the size of the table as last recorded in sqlite_stat1. + ** If any index is unanalyzed, then the threshold is -1 to + ** indicate a new, unanalyzed index + */ + szThreshold = pTab->nRowLogEst; + nIndex = 0; for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ + nIndex++; if( !pIdx->hasStat1 ){ - szThreshold = 0; /* Always analyze if any index lacks statistics */ - break; + szThreshold = -1; /* Always analyze if any index lacks statistics */ } } - if( szThreshold ){ - sqlite3OpenTable(pParse, iTabCur, iDb, pTab, OP_OpenRead); - sqlite3VdbeAddOp3(v, OP_IfSmaller, iTabCur, - sqlite3VdbeCurrentAddr(v)+2+(opMask&1), szThreshold); + + /* If table pTab has not been used in a way that would benefit from + ** having analysis statistics during the current session, then skip it, + ** unless the 0x10000 MASK bit is set. */ + if( (pTab->tabFlags & TF_MaybeReanalyze)!=0 ){ + /* Check for size change if stat1 has been used for a query */ + }else if( opMask & 0x10000 ){ + /* Check for size change if 0x10000 is set */ + }else if( pTab->pIndex!=0 && szThreshold<0 ){ + /* Do analysis if unanalyzed indexes exists */ + }else{ + /* Otherwise, we can skip this table */ + continue; + } + + nCheck++; + if( nCheck==2 ){ + /* If ANALYZE might be invoked two or more times, hold a write + ** transaction for efficiency */ + sqlite3BeginWriteOperation(pParse, 0, iDb); + } + nBtree += nIndex+1; + + /* Reanalyze if the table is 10 times larger or smaller than + ** the last analysis. Unconditional reanalysis if there are + ** unanalyzed indexes. */ + sqlite3OpenTable(pParse, iTabCur, iDb, pTab, OP_OpenRead); + if( szThreshold>=0 ){ + const LogEst iRange = 33; /* 10x size change */ + sqlite3VdbeAddOp4Int(v, OP_IfSizeBetween, iTabCur, + sqlite3VdbeCurrentAddr(v)+2+(opMask&1), + szThreshold>=iRange ? szThreshold-iRange : -1, + szThreshold+iRange); + VdbeCoverage(v); + }else{ + sqlite3VdbeAddOp2(v, OP_Rewind, iTabCur, + sqlite3VdbeCurrentAddr(v)+2+(opMask&1)); VdbeCoverage(v); } zSubSql = sqlite3MPrintf(db, "ANALYZE \"%w\".\"%w\"", @@ -140366,11 +141301,27 @@ SQLITE_PRIVATE void sqlite3Pragma( sqlite3VdbeAddOp4(v, OP_String8, 0, r1, 0, zSubSql, P4_DYNAMIC); sqlite3VdbeAddOp2(v, OP_ResultRow, r1, 1); }else{ - sqlite3VdbeAddOp4(v, OP_SqlExec, 0, 0, 0, zSubSql, P4_DYNAMIC); + sqlite3VdbeAddOp4(v, OP_SqlExec, nLimit ? 0x02 : 00, nLimit, 0, + zSubSql, P4_DYNAMIC); } } } sqlite3VdbeAddOp0(v, OP_Expire); + + /* In a schema with a large number of tables and indexes, scale back + ** the analysis_limit to avoid excess run-time in the worst case. + */ + if( !db->mallocFailed && nLimit>0 && nBtree>100 ){ + int iAddr, iEnd; + VdbeOp *aOp; + nLimit = 100*nLimit/nBtree; + if( nLimit<100 ) nLimit = 100; + aOp = sqlite3VdbeGetOp(v, 0); + iEnd = sqlite3VdbeCurrentAddr(v); + for(iAddr=0; iAddrnConstraint; i++, pConstraint++){ - if( pConstraint->usable==0 ) continue; - if( pConstraint->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue; if( pConstraint->iColumn < pTab->iHidden ) continue; + if( pConstraint->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue; + if( pConstraint->usable==0 ) return SQLITE_CONSTRAINT; j = pConstraint->iColumn - pTab->iHidden; assert( j < 2 ); seen[j] = i+1; @@ -140649,16 +141600,13 @@ static int pragmaVtabBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ j = seen[0]-1; pIdxInfo->aConstraintUsage[j].argvIndex = 1; pIdxInfo->aConstraintUsage[j].omit = 1; - if( seen[1]==0 ){ - pIdxInfo->estimatedCost = (double)1000; - pIdxInfo->estimatedRows = 1000; - return SQLITE_OK; - } pIdxInfo->estimatedCost = (double)20; pIdxInfo->estimatedRows = 20; - j = seen[1]-1; - pIdxInfo->aConstraintUsage[j].argvIndex = 2; - pIdxInfo->aConstraintUsage[j].omit = 1; + if( seen[1] ){ + j = seen[1]-1; + pIdxInfo->aConstraintUsage[j].argvIndex = 2; + pIdxInfo->aConstraintUsage[j].omit = 1; + } return SQLITE_OK; } @@ -140678,6 +141626,7 @@ static void pragmaVtabCursorClear(PragmaVtabCursor *pCsr){ int i; sqlite3_finalize(pCsr->pPragma); pCsr->pPragma = 0; + pCsr->iRowid = 0; for(i=0; iazArg); i++){ sqlite3_free(pCsr->azArg[i]); pCsr->azArg[i] = 0; @@ -141478,7 +142427,13 @@ SQLITE_PRIVATE void *sqlite3ParserAddCleanup( void (*xCleanup)(sqlite3*,void*), /* The cleanup routine */ void *pPtr /* Pointer to object to be cleaned up */ ){ - ParseCleanup *pCleanup = sqlite3DbMallocRaw(pParse->db, sizeof(*pCleanup)); + ParseCleanup *pCleanup; + if( sqlite3FaultSim(300) ){ + pCleanup = 0; + sqlite3OomFault(pParse->db); + }else{ + pCleanup = sqlite3DbMallocRaw(pParse->db, sizeof(*pCleanup)); + } if( pCleanup ){ pCleanup->pNext = pParse->pCleanup; pParse->pCleanup = pCleanup; @@ -143600,9 +144555,16 @@ static void generateSortTail( int addrExplain; /* Address of OP_Explain instruction */ #endif - ExplainQueryPlan2(addrExplain, (pParse, 0, - "USE TEMP B-TREE FOR %sORDER BY", pSort->nOBSat>0?"RIGHT PART OF ":"") - ); + nKey = pOrderBy->nExpr - pSort->nOBSat; + if( pSort->nOBSat==0 || nKey==1 ){ + ExplainQueryPlan2(addrExplain, (pParse, 0, + "USE TEMP B-TREE FOR %sORDER BY", pSort->nOBSat?"LAST TERM OF ":"" + )); + }else{ + ExplainQueryPlan2(addrExplain, (pParse, 0, + "USE TEMP B-TREE FOR LAST %d TERMS OF ORDER BY", nKey + )); + } sqlite3VdbeScanStatusRange(v, addrExplain,pSort->addrPush,pSort->addrPushEnd); sqlite3VdbeScanStatusCounters(v, addrExplain, addrExplain, pSort->addrPush); @@ -143640,7 +144602,6 @@ static void generateSortTail( regRow = sqlite3GetTempRange(pParse, nColumn); } } - nKey = pOrderBy->nExpr - pSort->nOBSat; if( pSort->sortFlags & SORTFLAG_UseSorter ){ int regSortOut = ++pParse->nMem; iSortTab = pParse->nTab++; @@ -144245,8 +145206,7 @@ SQLITE_PRIVATE void sqlite3SubqueryColumnTypes( NameContext sNC; assert( pSelect!=0 ); - testcase( (pSelect->selFlags & SF_Resolved)==0 ); - assert( (pSelect->selFlags & SF_Resolved)!=0 || IN_RENAME_OBJECT ); + assert( (pSelect->selFlags & SF_Resolved)!=0 ); 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; @@ -144257,17 +145217,22 @@ SQLITE_PRIVATE void sqlite3SubqueryColumnTypes( for(i=0, pCol=pTab->aCol; inCol; i++, pCol++){ const char *zType; i64 n; + int m = 0; + Select *pS2 = pSelect; pTab->tabFlags |= (pCol->colFlags & COLFLAG_NOINSERT); p = a[i].pExpr; /* pCol->szEst = ... // Column size est for SELECT tables never used */ pCol->affinity = sqlite3ExprAffinity(p); + while( pCol->affinity<=SQLITE_AFF_NONE && pS2->pNext!=0 ){ + m |= sqlite3ExprDataType(pS2->pEList->a[i].pExpr); + pS2 = pS2->pNext; + pCol->affinity = sqlite3ExprAffinity(pS2->pEList->a[i].pExpr); + } if( pCol->affinity<=SQLITE_AFF_NONE ){ pCol->affinity = aff; } - if( pCol->affinity>=SQLITE_AFF_TEXT && pSelect->pNext ){ - int m = 0; - Select *pS2; - for(m=0, pS2=pSelect->pNext; pS2; pS2=pS2->pNext){ + if( pCol->affinity>=SQLITE_AFF_TEXT && (pS2->pNext || pS2!=pSelect) ){ + for(pS2=pS2->pNext; pS2; pS2=pS2->pNext){ m |= sqlite3ExprDataType(pS2->pEList->a[i].pExpr); } if( pCol->affinity==SQLITE_AFF_TEXT && (m&0x01)!=0 ){ @@ -144297,12 +145262,12 @@ SQLITE_PRIVATE void sqlite3SubqueryColumnTypes( } } if( zType ){ - i64 m = sqlite3Strlen30(zType); + const i64 k = sqlite3Strlen30(zType); n = sqlite3Strlen30(pCol->zCnName); - pCol->zCnName = sqlite3DbReallocOrFree(db, pCol->zCnName, n+m+2); + pCol->zCnName = sqlite3DbReallocOrFree(db, pCol->zCnName, n+k+2); pCol->colFlags &= ~(COLFLAG_HASTYPE|COLFLAG_HASCOLL); if( pCol->zCnName ){ - memcpy(&pCol->zCnName[n+1], zType, m+1); + memcpy(&pCol->zCnName[n+1], zType, k+1); pCol->colFlags |= COLFLAG_HASTYPE; } } @@ -146699,7 +147664,7 @@ static void constInsert( ){ int i; assert( pColumn->op==TK_COLUMN ); - assert( sqlite3ExprIsConstant(pValue) ); + assert( sqlite3ExprIsConstant(pConst->pParse, pValue) ); if( ExprHasProperty(pColumn, EP_FixedCol) ) return; if( sqlite3ExprAffinity(pValue)!=0 ) return; @@ -146757,10 +147722,10 @@ static void findConstInWhere(WhereConst *pConst, Expr *pExpr){ pLeft = pExpr->pLeft; assert( pRight!=0 ); assert( pLeft!=0 ); - if( pRight->op==TK_COLUMN && sqlite3ExprIsConstant(pLeft) ){ + if( pRight->op==TK_COLUMN && sqlite3ExprIsConstant(pConst->pParse, pLeft) ){ constInsert(pConst,pRight,pLeft,pExpr); } - if( pLeft->op==TK_COLUMN && sqlite3ExprIsConstant(pRight) ){ + if( pLeft->op==TK_COLUMN && sqlite3ExprIsConstant(pConst->pParse, pRight) ){ constInsert(pConst,pLeft,pRight,pExpr); } } @@ -146981,6 +147946,18 @@ static int pushDownWindowCheck(Parse *pParse, Select *pSubq, Expr *pExpr){ ** The hope is that the terms added to the inner query will make it more ** efficient. ** +** NAME AMBIGUITY +** +** This optimization is called the "WHERE-clause push-down optimization". +** +** Do not confuse this optimization with another unrelated optimization +** with a similar name: The "MySQL push-down optimization" causes WHERE +** clause terms that can be evaluated using only the index and without +** reference to the table are run first, so that if they are false, +** unnecessary table seeks are avoided. +** +** RULES +** ** Do not attempt this optimization if: ** ** (1) (** This restriction was removed on 2017-09-29. We used to @@ -147046,10 +148023,10 @@ static int pushDownWindowCheck(Parse *pParse, Select *pSubq, Expr *pExpr){ ** (9c) There is a RIGHT JOIN (or FULL JOIN) in between the ON/USING ** clause and the subquery. ** -** Without this restriction, the push-down optimization might move -** the ON/USING filter expression from the left side of a RIGHT JOIN -** over to the right side, which leads to incorrect answers. See -** also restriction (6) in sqlite3ExprIsSingleTableConstraint(). +** Without this restriction, the WHERE-clause push-down optimization +** might move the ON/USING filter expression from the left side of a +** RIGHT JOIN over to the right side, which leads to incorrect answers. +** See also restriction (6) in sqlite3ExprIsSingleTableConstraint(). ** ** (10) The inner query is not the right-hand table of a RIGHT JOIN. ** @@ -147181,7 +148158,7 @@ static int pushDownWhereTerms( } #endif - if( sqlite3ExprIsSingleTableConstraint(pWhere, pSrcList, iSrc) ){ + if( sqlite3ExprIsSingleTableConstraint(pWhere, pSrcList, iSrc, 1) ){ nChng++; pSubq->selFlags |= SF_PushDown; while( pSubq ){ @@ -148316,8 +149293,7 @@ static void selectAddSubqueryTypeInfo(Walker *pWalker, Select *p){ 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 ); + assert( (p->selFlags & SF_Resolved) ); pTabList = p->pSrc; for(i=0, pFrom=pTabList->a; inSrc; i++, pFrom++){ Table *pTab = pFrom->pTab; @@ -148387,6 +149363,8 @@ SQLITE_PRIVATE void sqlite3SelectPrep( */ static void printAggInfo(AggInfo *pAggInfo){ int ii; + sqlite3DebugPrintf("AggInfo %d/%p:\n", + pAggInfo->selId, pAggInfo); for(ii=0; iinColumn; ii++){ struct AggInfo_col *pCol = &pAggInfo->aCol[ii]; sqlite3DebugPrintf( @@ -149577,7 +150555,7 @@ SQLITE_PRIVATE int sqlite3Select( /* Generate code for all sub-queries in the FROM clause */ pSub = pItem->pSelect; - if( pSub==0 ) continue; + if( pSub==0 || pItem->addrFillSub!=0 ) continue; /* The code for a subquery should only be generated once. */ assert( pItem->addrFillSub==0 ); @@ -149608,7 +150586,7 @@ SQLITE_PRIVATE int sqlite3Select( #endif assert( pItem->pSelect && (pItem->pSelect->selFlags & SF_PushDown)!=0 ); }else{ - TREETRACE(0x4000,pParse,p,("Push-down not possible\n")); + TREETRACE(0x4000,pParse,p,("WHERE-lcause push-down not possible\n")); } /* Convert unused result columns of the subquery into simple NULL @@ -150489,6 +151467,12 @@ select_end: sqlite3ExprListDelete(db, pMinMaxOrderBy); #ifdef SQLITE_DEBUG if( pAggInfo && !db->mallocFailed ){ +#if TREETRACE_ENABLED + if( sqlite3TreeTrace & 0x20 ){ + TREETRACE(0x20,pParse,p,("Finished with AggInfo\n")); + printAggInfo(pAggInfo); + } +#endif for(i=0; inColumn; i++){ Expr *pExpr = pAggInfo->aCol[i].pCExpr; if( pExpr==0 ) continue; @@ -151670,6 +152654,72 @@ static ExprList *sqlite3ExpandReturning( return pNew; } +/* If the Expr node is a subquery or an EXISTS operator or an IN operator that +** uses a subquery, and if the subquery is SF_Correlated, then mark the +** expression as EP_VarSelect. +*/ +static int sqlite3ReturningSubqueryVarSelect(Walker *NotUsed, Expr *pExpr){ + UNUSED_PARAMETER(NotUsed); + if( ExprUseXSelect(pExpr) + && (pExpr->x.pSelect->selFlags & SF_Correlated)!=0 + ){ + testcase( ExprHasProperty(pExpr, EP_VarSelect) ); + ExprSetProperty(pExpr, EP_VarSelect); + } + return WRC_Continue; +} + + +/* +** If the SELECT references the table pWalker->u.pTab, then do two things: +** +** (1) Mark the SELECT as as SF_Correlated. +** (2) Set pWalker->eCode to non-zero so that the caller will know +** that (1) has happened. +*/ +static int sqlite3ReturningSubqueryCorrelated(Walker *pWalker, Select *pSelect){ + int i; + SrcList *pSrc; + assert( pSelect!=0 ); + pSrc = pSelect->pSrc; + assert( pSrc!=0 ); + for(i=0; inSrc; i++){ + if( pSrc->a[i].pTab==pWalker->u.pTab ){ + testcase( pSelect->selFlags & SF_Correlated ); + pSelect->selFlags |= SF_Correlated; + pWalker->eCode = 1; + break; + } + } + return WRC_Continue; +} + +/* +** Scan the expression list that is the argument to RETURNING looking +** for subqueries that depend on the table which is being modified in the +** statement that is hosting the RETURNING clause (pTab). Mark all such +** subqueries as SF_Correlated. If the subqueries are part of an +** expression, mark the expression as EP_VarSelect. +** +** https://sqlite.org/forum/forumpost/2c83569ce8945d39 +*/ +static void sqlite3ProcessReturningSubqueries( + ExprList *pEList, + Table *pTab +){ + Walker w; + memset(&w, 0, sizeof(w)); + w.xExprCallback = sqlite3ExprWalkNoop; + w.xSelectCallback = sqlite3ReturningSubqueryCorrelated; + w.u.pTab = pTab; + sqlite3WalkExprList(&w, pEList); + if( w.eCode ){ + w.xExprCallback = sqlite3ReturningSubqueryVarSelect; + w.xSelectCallback = sqlite3SelectWalkNoop; + sqlite3WalkExprList(&w, pEList); + } +} + /* ** Generate code for the RETURNING trigger. Unlike other triggers ** that invoke a subprogram in the bytecode, the code for RETURNING @@ -151706,6 +152756,7 @@ static void codeReturningTrigger( sSelect.pSrc = &sFrom; sFrom.nSrc = 1; sFrom.a[0].pTab = pTab; + sFrom.a[0].zName = pTab->zName; /* tag-20240424-1 */ sFrom.a[0].iCursor = -1; sqlite3SelectPrep(pParse, &sSelect, 0); if( pParse->nErr==0 ){ @@ -151732,6 +152783,7 @@ static void codeReturningTrigger( int i; int nCol = pNew->nExpr; int reg = pParse->nMem+1; + sqlite3ProcessReturningSubqueries(pNew, pTab); pParse->nMem += nCol+2; pReturning->iRetReg = reg; for(i=0; ipVtabCtx = &sCtx; pTab->nTabRef++; rc = xConstruct(db, pMod->pAux, nArg, azArg, &pVTable->pVtab, &zErr); + assert( pTab!=0 ); + assert( pTab->nTabRef>1 || rc!=SQLITE_OK ); sqlite3DeleteTable(db, pTab); db->pVtabCtx = sCtx.pPrior; if( rc==SQLITE_NOMEM ) sqlite3OomFault(db); @@ -154964,7 +156018,7 @@ static int vtabCallConstructor( pVTable->nRef = 1; if( sCtx.bDeclared==0 ){ const char *zFormat = "vtable constructor did not declare schema: %s"; - *pzErr = sqlite3MPrintf(db, zFormat, pTab->zName); + *pzErr = sqlite3MPrintf(db, zFormat, zModuleName); sqlite3VtabUnlock(pVTable); rc = SQLITE_ERROR; }else{ @@ -155142,12 +156196,30 @@ SQLITE_API int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){ Table *pTab; Parse sParse; int initBusy; + int i; + const unsigned char *z; + static const u8 aKeyword[] = { TK_CREATE, TK_TABLE, 0 }; #ifdef SQLITE_ENABLE_API_ARMOR if( !sqlite3SafetyCheckOk(db) || zCreateTable==0 ){ return SQLITE_MISUSE_BKPT; } #endif + + /* Verify that the first two keywords in the CREATE TABLE statement + ** really are "CREATE" and "TABLE". If this is not the case, then + ** sqlite3_declare_vtab() is being misused. + */ + z = (const unsigned char*)zCreateTable; + for(i=0; aKeyword[i]; i++){ + int tokenType = 0; + do{ z += sqlite3GetToken(z, &tokenType); }while( tokenType==TK_SPACE ); + if( tokenType!=aKeyword[i] ){ + sqlite3ErrorWithMsg(db, SQLITE_ERROR, "syntax error"); + return SQLITE_ERROR; + } + } + sqlite3_mutex_enter(db->mutex); pCtx = db->pVtabCtx; if( !pCtx || pCtx->bDeclared ){ @@ -155155,6 +156227,7 @@ SQLITE_API int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){ sqlite3_mutex_leave(db->mutex); return SQLITE_MISUSE_BKPT; } + pTab = pCtx->pTab; assert( IsVirtual(pTab) ); @@ -155168,11 +156241,10 @@ SQLITE_API int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){ initBusy = db->init.busy; db->init.busy = 0; sParse.nQueryLoop = 1; - if( SQLITE_OK==sqlite3RunParser(&sParse, zCreateTable) - && ALWAYS(sParse.pNewTable!=0) - && ALWAYS(!db->mallocFailed) - && IsOrdinaryTable(sParse.pNewTable) - ){ + if( SQLITE_OK==sqlite3RunParser(&sParse, zCreateTable) ){ + assert( sParse.pNewTable!=0 ); + assert( !db->mallocFailed ); + assert( IsOrdinaryTable(sParse.pNewTable) ); assert( sParse.zErrMsg==0 ); if( !pTab->aCol ){ Table *pNew = sParse.pNewTable; @@ -157667,6 +158739,27 @@ static SQLITE_NOINLINE void filterPullDown( } } +/* +** Loop pLoop is a WHERE_INDEXED level that uses at least one IN(...) +** operator. Return true if level pLoop is guaranteed to visit only one +** row for each key generated for the index. +*/ +static int whereLoopIsOneRow(WhereLoop *pLoop){ + if( pLoop->u.btree.pIndex->onError + && pLoop->nSkip==0 + && pLoop->u.btree.nEq==pLoop->u.btree.pIndex->nKeyCol + ){ + int ii; + for(ii=0; iiu.btree.nEq; ii++){ + if( pLoop->aLTerm[ii]->eOperator & (WO_IS|WO_ISNULL) ){ + return 0; + } + } + return 1; + } + return 0; +} + /* ** Generate code for the start of the iLevel-th loop in the WHERE clause ** implementation described by pWInfo. @@ -157745,7 +158838,7 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( if( pLevel->iFrom>0 && (pTabItem[0].fg.jointype & JT_LEFT)!=0 ){ pLevel->iLeftJoin = ++pParse->nMem; sqlite3VdbeAddOp2(v, OP_Integer, 0, pLevel->iLeftJoin); - VdbeComment((v, "init LEFT JOIN no-match flag")); + VdbeComment((v, "init LEFT JOIN match flag")); } /* Compute a safe address to jump to if we discover that the table for @@ -158414,7 +159507,9 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( } /* Record the instruction used to terminate the loop. */ - if( pLoop->wsFlags & WHERE_ONEROW ){ + if( (pLoop->wsFlags & WHERE_ONEROW) + || (pLevel->u.in.nIn && regBignull==0 && whereLoopIsOneRow(pLoop)) + ){ pLevel->op = OP_Noop; }else if( bRev ){ pLevel->op = OP_Prev; @@ -158804,6 +159899,12 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( ** iLoop==3: Code all remaining expressions. ** ** An effort is made to skip unnecessary iterations of the loop. + ** + ** This optimization of causing simple query restrictions to occur before + ** more complex one is call the "push-down" optimization in MySQL. Here + ** in SQLite, the name is "MySQL push-down", since there is also another + ** totally unrelated optimization called "WHERE-clause push-down". + ** Sometimes the qualifier is omitted, resulting in an ambiguity, so beware. */ iLoop = (pIdx ? 1 : 2); do{ @@ -159054,7 +160155,16 @@ SQLITE_PRIVATE SQLITE_NOINLINE void sqlite3WhereRightJoinLoop( pRJ->regReturn); for(k=0; ka[k].pWLoop->iTab == pWInfo->a[k].iFrom ); + pRight = &pWInfo->pTabList->a[pWInfo->a[k].iFrom]; mAll |= pWInfo->a[k].pWLoop->maskSelf; + if( pRight->fg.viaCoroutine ){ + sqlite3VdbeAddOp3( + v, OP_Null, 0, pRight->regResult, + pRight->regResult + pRight->pSelect->pEList->nExpr-1 + ); + } sqlite3VdbeAddOp1(v, OP_NullRow, pWInfo->a[k].iTabCur); iIdxCur = pWInfo->a[k].iIdxCur; if( iIdxCur ){ @@ -160111,7 +161221,7 @@ static SQLITE_NOINLINE int exprMightBeIndexed2( if( pIdx->aiColumn[i]!=XN_EXPR ) continue; assert( pIdx->bHasExpr ); if( sqlite3ExprCompareSkip(pExpr,pIdx->aColExpr->a[i].pExpr,iCur)==0 - && pExpr->op!=TK_STRING + && !sqlite3ExprIsConstant(0,pIdx->aColExpr->a[i].pExpr) ){ aiCurCol[0] = iCur; aiCurCol[1] = XN_EXPR; @@ -160760,6 +161870,7 @@ SQLITE_PRIVATE void SQLITE_NOINLINE sqlite3WhereAddLimit(WhereClause *pWC, Selec continue; } if( pWC->a[ii].leftCursor!=iCsr ) return; + if( pWC->a[ii].prereqRight!=0 ) return; } /* Check condition (5). Return early if it is not met. */ @@ -160774,12 +161885,14 @@ SQLITE_PRIVATE void SQLITE_NOINLINE sqlite3WhereAddLimit(WhereClause *pWC, Selec /* All conditions are met. Add the terms to the where-clause object. */ assert( p->pLimit->op==TK_LIMIT ); - whereAddLimitExpr(pWC, p->iLimit, p->pLimit->pLeft, - iCsr, SQLITE_INDEX_CONSTRAINT_LIMIT); - if( p->iOffset>0 ){ + if( p->iOffset!=0 && (p->selFlags & SF_Compound)==0 ){ whereAddLimitExpr(pWC, p->iOffset, p->pLimit->pRight, iCsr, SQLITE_INDEX_CONSTRAINT_OFFSET); } + if( p->iOffset==0 || (p->selFlags & SF_Compound)==0 ){ + whereAddLimitExpr(pWC, p->iLimit, p->pLimit->pLeft, + iCsr, SQLITE_INDEX_CONSTRAINT_LIMIT); + } } } @@ -161297,6 +162410,42 @@ static Expr *whereRightSubexprIsColumn(Expr *p){ return 0; } +/* +** Term pTerm is guaranteed to be a WO_IN term. It may be a component term +** of a vector IN expression of the form "(x, y, ...) IN (SELECT ...)". +** This function checks to see if the term is compatible with an index +** column with affinity idxaff (one of the SQLITE_AFF_XYZ values). If so, +** it returns a pointer to the name of the collation sequence (e.g. "BINARY" +** or "NOCASE") used by the comparison in pTerm. If it is not compatible +** with affinity idxaff, NULL is returned. +*/ +static SQLITE_NOINLINE const char *indexInAffinityOk( + Parse *pParse, + WhereTerm *pTerm, + u8 idxaff +){ + Expr *pX = pTerm->pExpr; + Expr inexpr; + + assert( pTerm->eOperator & WO_IN ); + + if( sqlite3ExprIsVector(pX->pLeft) ){ + int iField = pTerm->u.x.iField - 1; + inexpr.flags = 0; + inexpr.op = TK_EQ; + inexpr.pLeft = pX->pLeft->x.pList->a[iField].pExpr; + assert( ExprUseXSelect(pX) ); + inexpr.pRight = pX->x.pSelect->pEList->a[iField].pExpr; + pX = &inexpr; + } + + if( sqlite3IndexAffinityOk(pX, idxaff) ){ + CollSeq *pRet = sqlite3ExprCompareCollSeq(pParse, pX); + return pRet ? pRet->zName : sqlite3StrBINARY; + } + return 0; +} + /* ** Advance to the next WhereTerm that matches according to the criteria ** established when the pScan object was initialized by whereScanInit(). @@ -161347,16 +162496,24 @@ static WhereTerm *whereScanNext(WhereScan *pScan){ if( (pTerm->eOperator & pScan->opMask)!=0 ){ /* Verify the affinity and collating sequence match */ if( pScan->zCollName && (pTerm->eOperator & WO_ISNULL)==0 ){ - CollSeq *pColl; + const char *zCollName; Parse *pParse = pWC->pWInfo->pParse; pX = pTerm->pExpr; - if( !sqlite3IndexAffinityOk(pX, pScan->idxaff) ){ - continue; + + if( (pTerm->eOperator & WO_IN) ){ + zCollName = indexInAffinityOk(pParse, pTerm, pScan->idxaff); + if( !zCollName ) continue; + }else{ + CollSeq *pColl; + if( !sqlite3IndexAffinityOk(pX, pScan->idxaff) ){ + continue; + } + assert(pX->pLeft); + pColl = sqlite3ExprCompareCollSeq(pParse, pX); + zCollName = pColl ? pColl->zName : sqlite3StrBINARY; } - assert(pX->pLeft); - pColl = sqlite3ExprCompareCollSeq(pParse, pX); - if( pColl==0 ) pColl = pParse->db->pDfltColl; - if( sqlite3StrICmp(pColl->zName, pScan->zCollName) ){ + + if( sqlite3StrICmp(zCollName, pScan->zCollName) ){ continue; } } @@ -161708,9 +162865,13 @@ static void translateColumnToCopy( ** are no-ops. */ #if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(WHERETRACE_ENABLED) -static void whereTraceIndexInfoInputs(sqlite3_index_info *p){ +static void whereTraceIndexInfoInputs( + sqlite3_index_info *p, /* The IndexInfo object */ + Table *pTab /* The TABLE that is the virtual table */ +){ int i; if( (sqlite3WhereTrace & 0x10)==0 ) return; + sqlite3DebugPrintf("sqlite3_index_info inputs for %s:\n", pTab->zName); for(i=0; inConstraint; i++){ sqlite3DebugPrintf( " constraint[%d]: col=%d termid=%d op=%d usabled=%d collseq=%s\n", @@ -161728,9 +162889,13 @@ static void whereTraceIndexInfoInputs(sqlite3_index_info *p){ p->aOrderBy[i].desc); } } -static void whereTraceIndexInfoOutputs(sqlite3_index_info *p){ +static void whereTraceIndexInfoOutputs( + sqlite3_index_info *p, /* The IndexInfo object */ + Table *pTab /* The TABLE that is the virtual table */ +){ int i; if( (sqlite3WhereTrace & 0x10)==0 ) return; + sqlite3DebugPrintf("sqlite3_index_info outputs for %s:\n", pTab->zName); for(i=0; inConstraint; i++){ sqlite3DebugPrintf(" usage[%d]: argvIdx=%d omit=%d\n", i, @@ -161744,8 +162909,8 @@ static void whereTraceIndexInfoOutputs(sqlite3_index_info *p){ sqlite3DebugPrintf(" estimatedRows=%lld\n", p->estimatedRows); } #else -#define whereTraceIndexInfoInputs(A) -#define whereTraceIndexInfoOutputs(A) +#define whereTraceIndexInfoInputs(A,B) +#define whereTraceIndexInfoOutputs(A,B) #endif /* @@ -161929,7 +163094,7 @@ static SQLITE_NOINLINE void constructAutomaticIndex( ** WHERE clause (or the ON clause of a LEFT join) that constrain which ** rows of the target table (pSrc) that can be used. */ if( (pTerm->wtFlags & TERM_VIRTUAL)==0 - && sqlite3ExprIsSingleTableConstraint(pExpr, pTabList, pLevel->iFrom) + && sqlite3ExprIsSingleTableConstraint(pExpr, pTabList, pLevel->iFrom, 0) ){ pPartial = sqlite3ExprAnd(pParse, pPartial, sqlite3ExprDup(pParse->db, pExpr, 0)); @@ -161971,7 +163136,7 @@ static SQLITE_NOINLINE void constructAutomaticIndex( ** if they go out of sync. */ if( IsView(pTable) ){ - extraCols = ALLBITS; + extraCols = ALLBITS & ~idxCols; }else{ extraCols = pSrc->colUsed & (~idxCols | MASKBIT(BMS-1)); } @@ -162198,7 +163363,7 @@ static SQLITE_NOINLINE void sqlite3ConstructBloomFilter( for(pTerm=pWInfo->sWC.a; pTermpExpr; if( (pTerm->wtFlags & TERM_VIRTUAL)==0 - && sqlite3ExprIsSingleTableConstraint(pExpr, pTabList, iSrc) + && sqlite3ExprIsSingleTableConstraint(pExpr, pTabList, iSrc, 0) ){ sqlite3ExprIfFalse(pParse, pTerm->pExpr, addrCont, SQLITE_JUMPIFNULL); } @@ -162324,7 +163489,7 @@ static sqlite3_index_info *allocateIndexInfo( Expr *pE2; /* Skip over constant terms in the ORDER BY clause */ - if( sqlite3ExprIsConstant(pExpr) ){ + if( sqlite3ExprIsConstant(0, pExpr) ){ continue; } @@ -162359,7 +163524,7 @@ static sqlite3_index_info *allocateIndexInfo( } if( i==n ){ nOrderBy = n; - if( (pWInfo->wctrlFlags & WHERE_DISTINCTBY) ){ + if( (pWInfo->wctrlFlags & WHERE_DISTINCTBY) && !pSrc->fg.rowidUsed ){ eDistinct = 2 + ((pWInfo->wctrlFlags & WHERE_SORTBYGROUP)!=0); }else if( pWInfo->wctrlFlags & WHERE_GROUPBY ){ eDistinct = 1; @@ -162436,7 +163601,7 @@ static sqlite3_index_info *allocateIndexInfo( pIdxInfo->nConstraint = j; for(i=j=0; ia[i].pExpr; - if( sqlite3ExprIsConstant(pExpr) ) continue; + if( sqlite3ExprIsConstant(0, pExpr) ) continue; assert( pExpr->op==TK_COLUMN || (pExpr->op==TK_COLLATE && pExpr->pLeft->op==TK_COLUMN && pExpr->iColumn==pExpr->pLeft->iColumn) ); @@ -162488,11 +163653,11 @@ static int vtabBestIndex(Parse *pParse, Table *pTab, sqlite3_index_info *p){ sqlite3_vtab *pVtab = sqlite3GetVTable(pParse->db, pTab)->pVtab; int rc; - whereTraceIndexInfoInputs(p); + whereTraceIndexInfoInputs(p, pTab); pParse->db->nSchemaLock++; rc = pVtab->pModule->xBestIndex(pVtab, p); pParse->db->nSchemaLock--; - whereTraceIndexInfoOutputs(p); + whereTraceIndexInfoOutputs(p, pTab); if( rc!=SQLITE_OK && rc!=SQLITE_CONSTRAINT ){ if( rc==SQLITE_NOMEM ){ @@ -163970,7 +165135,9 @@ static int whereLoopAddBtreeIndex( } 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); + if( pProbe->bLowQual && pSrc->fg.isIndexedBy==0 ){ + opMask &= ~(WO_EQ|WO_IN|WO_IS); + } } assert( pNew->u.btree.nEqnColumn ); @@ -164237,10 +165404,13 @@ static int whereLoopAddBtreeIndex( } } - /* Set rCostIdx to the cost of visiting selected rows in index. Add - ** it to pNew->rRun, which is currently set to the cost of the index - ** seek only. Then, if this is a non-covering index, add the cost of - ** visiting the rows in the main table. */ + /* Set rCostIdx to the estimated cost of visiting selected rows in the + ** index. The estimate is the sum of two values: + ** 1. The cost of doing one search-by-key to find the first matching + ** entry + ** 2. Stepping forward in the index pNew->nOut times to find all + ** additional matching entries. + */ assert( pSrc->pTab->szTabRow>0 ); if( pProbe->idxType==SQLITE_IDXTYPE_IPK ){ /* The pProbe->szIdxRow is low for an IPK table since the interior @@ -164251,7 +165421,15 @@ static int whereLoopAddBtreeIndex( }else{ rCostIdx = pNew->nOut + 1 + (15*pProbe->szIdxRow)/pSrc->pTab->szTabRow; } - pNew->rRun = sqlite3LogEstAdd(rLogSize, rCostIdx); + rCostIdx = sqlite3LogEstAdd(rLogSize, rCostIdx); + + /* Estimate the cost of running the loop. If all data is coming + ** from the index, then this is just the cost of doing the index + ** lookup and scan. But if some data is coming out of the main table, + ** we also have to add in the cost of doing pNew->nOut searches to + ** locate the row in the main table that corresponds to the index entry. + */ + pNew->rRun = rCostIdx; if( (pNew->wsFlags & (WHERE_IDX_ONLY|WHERE_IPK|WHERE_EXPRIDX))==0 ){ pNew->rRun = sqlite3LogEstAdd(pNew->rRun, pNew->nOut + 16); } @@ -164357,7 +165535,9 @@ static int indexMightHelpWithOrderBy( for(ii=0; iinExpr; ii++){ Expr *pExpr = sqlite3ExprSkipCollateAndLikely(pOB->a[ii].pExpr); if( NEVER(pExpr==0) ) continue; - if( pExpr->op==TK_COLUMN && pExpr->iTable==iCursor ){ + if( (pExpr->op==TK_COLUMN || pExpr->op==TK_AGG_COLUMN) + && pExpr->iTable==iCursor + ){ if( pExpr->iColumn<0 ) return 1; for(jj=0; jjnKeyCol; jj++){ if( pExpr->iColumn==pIndex->aiColumn[jj] ) return 1; @@ -164614,7 +165794,7 @@ static void wherePartIdxExpr( u8 aff; if( pLeft->op!=TK_COLUMN ) return; - if( !sqlite3ExprIsConstant(pRight) ) return; + if( !sqlite3ExprIsConstant(0, pRight) ) return; if( !sqlite3IsBinary(sqlite3ExprCompareCollSeq(pParse, pPart)) ) return; if( pLeft->iColumn<0 ) return; aff = pIdx->pTable->aCol[pLeft->iColumn].affinity; @@ -164963,7 +166143,7 @@ static int whereLoopAddBtree( ** unique index is used (making the index functionally non-unique) ** then the sqlite_stat1 data becomes important for scoring the ** plan */ - pTab->tabFlags |= TF_StatsUsed; + pTab->tabFlags |= TF_MaybeReanalyze; } #ifdef SQLITE_ENABLE_STAT4 sqlite3Stat4ProbeFree(pBuilder->pRec); @@ -164985,6 +166165,21 @@ static int isLimitTerm(WhereTerm *pTerm){ && pTerm->eMatchOp<=SQLITE_INDEX_CONSTRAINT_OFFSET; } +/* +** Return true if the first nCons constraints in the pUsage array are +** marked as in-use (have argvIndex>0). False otherwise. +*/ +static int allConstraintsUsed( + struct sqlite3_index_constraint_usage *aUsage, + int nCons +){ + int ii; + for(ii=0; iipNew->iTab. This @@ -165125,13 +166320,20 @@ static int whereLoopAddVirtualOne( *pbIn = 1; assert( (mExclude & WO_IN)==0 ); } + /* Unless pbRetryLimit is non-NULL, there should be no LIMIT/OFFSET + ** terms. And if there are any, they should follow all other terms. */ assert( pbRetryLimit || !isLimitTerm(pTerm) ); - if( isLimitTerm(pTerm) && *pbIn ){ + assert( !isLimitTerm(pTerm) || i>=nConstraint-2 ); + assert( !isLimitTerm(pTerm) || i==nConstraint-1 || isLimitTerm(pTerm+1) ); + + if( isLimitTerm(pTerm) && (*pbIn || !allConstraintsUsed(pUsage, i)) ){ /* If there is an IN(...) term handled as an == (separate call to ** xFilter for each value on the RHS of the IN) and a LIMIT or - ** OFFSET term handled as well, the plan is unusable. Set output - ** variable *pbRetryLimit to true to tell the caller to retry with - ** LIMIT and OFFSET disabled. */ + ** OFFSET term handled as well, the plan is unusable. Similarly, + ** if there is a LIMIT/OFFSET and there are other unused terms, + ** the plan cannot be used. In these cases set variable *pbRetryLimit + ** to true to tell the caller to retry with LIMIT and OFFSET + ** disabled. */ if( pIdxInfo->needToFreeIdxStr ){ sqlite3_free(pIdxInfo->idxStr); pIdxInfo->idxStr = 0; @@ -165988,7 +167190,7 @@ static i8 wherePathSatisfiesOrderBy( if( MASKBIT(i) & obSat ) continue; p = pOrderBy->a[i].pExpr; mTerm = sqlite3WhereExprUsage(&pWInfo->sMaskSet,p); - if( mTerm==0 && !sqlite3ExprIsConstant(p) ) continue; + if( mTerm==0 && !sqlite3ExprIsConstant(0,p) ) continue; if( (mTerm&~orderDistinctMask)==0 ){ obSat |= MASKBIT(i); } @@ -166457,10 +167659,9 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ if( pFrom->isOrdered==pWInfo->pOrderBy->nExpr ){ pWInfo->eDistinct = WHERE_DISTINCT_ORDERED; } - if( pWInfo->pSelect->pOrderBy - && pWInfo->nOBSat > pWInfo->pSelect->pOrderBy->nExpr ){ - pWInfo->nOBSat = pWInfo->pSelect->pOrderBy->nExpr; - } + /* vvv--- See check-in [12ad822d9b827777] on 2023-03-16 ---vvv */ + assert( pWInfo->pSelect->pOrderBy==0 + || pWInfo->nOBSat <= pWInfo->pSelect->pOrderBy->nExpr ); }else{ pWInfo->revMask = pFrom->revLoop; if( pWInfo->nOBSat<=0 ){ @@ -166503,7 +167704,6 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ } } - pWInfo->nRowOut = pFrom->nRow; /* Free temporary memory and return success */ @@ -166511,6 +167711,83 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){ return SQLITE_OK; } +/* +** This routine implements a heuristic designed to improve query planning. +** This routine is called in between the first and second call to +** wherePathSolver(). Hence the name "Interstage" "Heuristic". +** +** The first call to wherePathSolver() (hereafter just "solver()") computes +** the best path without regard to the order of the outputs. The second call +** to the solver() builds upon the first call to try to find an alternative +** path that satisfies the ORDER BY clause. +** +** This routine looks at the results of the first solver() run, and for +** every FROM clause term in the resulting query plan that uses an equality +** constraint against an index, disable other WhereLoops for that same +** FROM clause term that would try to do a full-table scan. This prevents +** an index search from being converted into a full-table scan in order to +** satisfy an ORDER BY clause, since even though we might get slightly better +** performance using the full-scan without sorting if the output size +** estimates are very precise, we might also get severe performance +** degradation using the full-scan if the output size estimate is too large. +** It is better to err on the side of caution. +** +** Except, if the first solver() call generated a full-table scan in an outer +** loop then stop this analysis at the first full-scan, since the second +** solver() run might try to swap that full-scan for another in order to +** get the output into the correct order. In other words, we allow a +** rewrite like this: +** +** First Solver() Second Solver() +** |-- SCAN t1 |-- SCAN t2 +** |-- SEARCH t2 `-- SEARCH t1 +** `-- SORT USING B-TREE +** +** The purpose of this routine is to disallow rewrites such as: +** +** First Solver() Second Solver() +** |-- SEARCH t1 |-- SCAN t2 <--- bad! +** |-- SEARCH t2 `-- SEARCH t1 +** `-- SORT USING B-TREE +** +** See test cases in test/whereN.test for the real-world query that +** originally provoked this heuristic. +*/ +static SQLITE_NOINLINE void whereInterstageHeuristic(WhereInfo *pWInfo){ + int i; +#ifdef WHERETRACE_ENABLED + int once = 0; +#endif + for(i=0; inLevel; i++){ + WhereLoop *p = pWInfo->a[i].pWLoop; + if( p==0 ) break; + if( (p->wsFlags & WHERE_VIRTUALTABLE)!=0 ) continue; + if( (p->wsFlags & (WHERE_COLUMN_EQ|WHERE_COLUMN_NULL|WHERE_COLUMN_IN))!=0 ){ + u8 iTab = p->iTab; + WhereLoop *pLoop; + for(pLoop=pWInfo->pLoops; pLoop; pLoop=pLoop->pNextLoop){ + if( pLoop->iTab!=iTab ) continue; + if( (pLoop->wsFlags & (WHERE_CONSTRAINT|WHERE_AUTO_INDEX))!=0 ){ + /* Auto-index and index-constrained loops allowed to remain */ + continue; + } +#ifdef WHERETRACE_ENABLED + if( sqlite3WhereTrace & 0x80 ){ + if( once==0 ){ + sqlite3DebugPrintf("Loops disabled by interstage heuristic:\n"); + once = 1; + } + sqlite3WhereLoopPrint(pLoop, &pWInfo->sWC); + } +#endif /* WHERETRACE_ENABLED */ + pLoop->prereq = ALLBITS; /* Prevent 2nd solver() from using this one */ + } + }else{ + break; + } + } +} + /* ** Most queries use only a single table (they are not joins) and have ** simple == constraints against indexed fields. This routine attempts @@ -166799,7 +168076,7 @@ static SQLITE_NOINLINE void whereCheckIfBloomFilterIsUseful( SrcItem *pItem = &pWInfo->pTabList->a[pLoop->iTab]; Table *pTab = pItem->pTab; if( (pTab->tabFlags & TF_HasStat1)==0 ) break; - pTab->tabFlags |= TF_StatsUsed; + pTab->tabFlags |= TF_MaybeReanalyze; if( i>=1 && (pLoop->wsFlags & reqFlags)==reqFlags /* vvvvvv--- Always the case if WHERE_COLUMN_EQ is defined */ @@ -166820,6 +168097,58 @@ static SQLITE_NOINLINE void whereCheckIfBloomFilterIsUseful( } } +/* +** Expression Node callback for sqlite3ExprCanReturnSubtype(). +** +** Only a function call is able to return a subtype. So if the node +** is not a function call, return WRC_Prune immediately. +** +** A function call is able to return a subtype if it has the +** SQLITE_RESULT_SUBTYPE property. +** +** Assume that every function is able to pass-through a subtype from +** one of its argument (using sqlite3_result_value()). Most functions +** are not this way, but we don't have a mechanism to distinguish those +** that are from those that are not, so assume they all work this way. +** That means that if one of its arguments is another function and that +** other function is able to return a subtype, then this function is +** able to return a subtype. +*/ +static int exprNodeCanReturnSubtype(Walker *pWalker, Expr *pExpr){ + int n; + FuncDef *pDef; + sqlite3 *db; + if( pExpr->op!=TK_FUNCTION ){ + return WRC_Prune; + } + assert( ExprUseXList(pExpr) ); + db = pWalker->pParse->db; + 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 ){ + pWalker->eCode = 1; + return WRC_Prune; + } + return WRC_Continue; +} + +/* +** Return TRUE if expression pExpr is able to return a subtype. +** +** A TRUE return does not guarantee that a subtype will be returned. +** It only indicates that a subtype return is possible. False positives +** are acceptable as they only disable an optimization. False negatives, +** on the other hand, can lead to incorrect answers. +*/ +static int sqlite3ExprCanReturnSubtype(Parse *pParse, Expr *pExpr){ + Walker w; + memset(&w, 0, sizeof(w)); + w.pParse = pParse; + w.xExprCallback = exprNodeCanReturnSubtype; + sqlite3WalkExpr(&w, pExpr); + return w.eCode; +} + /* ** 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 @@ -166852,20 +168181,12 @@ static SQLITE_NOINLINE void whereAddIndexedExpr( }else{ continue; } - if( sqlite3ExprIsConstant(pExpr) ) continue; - if( pExpr->op==TK_FUNCTION ){ + if( sqlite3ExprIsConstant(0,pExpr) ) continue; + if( pExpr->op==TK_FUNCTION && sqlite3ExprCanReturnSubtype(pParse,pExpr) ){ /* 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; - } + continue; } p = sqlite3DbMallocRaw(pParse->db, sizeof(IndexedExpr)); if( p==0 ) break; @@ -167130,7 +168451,11 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( ){ pWInfo->eDistinct = WHERE_DISTINCT_UNIQUE; } - ExplainQueryPlan((pParse, 0, "SCAN CONSTANT ROW")); + if( ALWAYS(pWInfo->pSelect) + && (pWInfo->pSelect->selFlags & SF_MultiValue)==0 + ){ + ExplainQueryPlan((pParse, 0, "SCAN CONSTANT ROW")); + } }else{ /* Assign a bit from the bitmask to every term in the FROM clause. ** @@ -167283,6 +168608,7 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( wherePathSolver(pWInfo, 0); if( db->mallocFailed ) goto whereBeginError; if( pWInfo->pOrderBy ){ + whereInterstageHeuristic(pWInfo); wherePathSolver(pWInfo, pWInfo->nRowOut+1); if( db->mallocFailed ) goto whereBeginError; } @@ -167832,7 +169158,15 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){ addr = sqlite3VdbeAddOp1(v, OP_IfPos, pLevel->iLeftJoin); VdbeCoverage(v); assert( (ws & WHERE_IDX_ONLY)==0 || (ws & WHERE_INDEXED)!=0 ); if( (ws & WHERE_IDX_ONLY)==0 ){ - assert( pLevel->iTabCur==pTabList->a[pLevel->iFrom].iCursor ); + SrcItem *pSrc = &pTabList->a[pLevel->iFrom]; + assert( pLevel->iTabCur==pSrc->iCursor ); + if( pSrc->fg.viaCoroutine ){ + int m, n; + n = pSrc->regResult; + assert( pSrc->pTab!=0 ); + m = pSrc->pTab->nCol; + sqlite3VdbeAddOp3(v, OP_Null, 0, n, n+m-1); + } sqlite3VdbeAddOp1(v, OP_NullRow, pLevel->iTabCur); } if( (ws & WHERE_INDEXED) @@ -167882,6 +169216,7 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){ */ if( pTabItem->fg.viaCoroutine ){ testcase( pParse->db->mallocFailed ); + assert( pTabItem->regResult>=0 ); translateColumnToCopy(pParse, pLevel->addrBody, pLevel->iTabCur, pTabItem->regResult, 0); continue; @@ -169186,7 +170521,7 @@ SQLITE_PRIVATE void sqlite3WindowListDelete(sqlite3 *db, Window *p){ ** variable values in the expression tree. */ static Expr *sqlite3WindowOffsetExpr(Parse *pParse, Expr *pExpr){ - if( 0==sqlite3ExprIsConstant(pExpr) ){ + if( 0==sqlite3ExprIsConstant(0,pExpr) ){ if( IN_RENAME_OBJECT ) sqlite3RenameExprUnmap(pParse, pExpr); sqlite3ExprDelete(pParse->db, pExpr); pExpr = sqlite3ExprAlloc(pParse->db, TK_NULL, 0, 0); @@ -171278,6 +172613,14 @@ static void updateDeleteLimitError( return pSelect; } + /* Memory allocator for parser stack resizing. This is a thin wrapper around + ** sqlite3_realloc() that includes a call to sqlite3FaultSim() to facilitate + ** testing. + */ + static void *parserStackRealloc(void *pOld, sqlite3_uint64 newSize){ + return sqlite3FaultSim(700) ? 0 : sqlite3_realloc(pOld, newSize); + } + /* Construct a new Expr object from a single token */ static Expr *tokenExpr(Parse *pParse, int op, Token t){ @@ -171527,8 +172870,8 @@ static void updateDeleteLimitError( #define TK_TRUEFALSE 170 #define TK_ISNOT 171 #define TK_FUNCTION 172 -#define TK_UMINUS 173 -#define TK_UPLUS 174 +#define TK_UPLUS 173 +#define TK_UMINUS 174 #define TK_TRUTH 175 #define TK_REGISTER 176 #define TK_VECTOR 177 @@ -171537,8 +172880,9 @@ static void updateDeleteLimitError( #define TK_ASTERISK 180 #define TK_SPAN 181 #define TK_ERROR 182 -#define TK_SPACE 183 -#define TK_ILLEGAL 184 +#define TK_QNUMBER 183 +#define TK_SPACE 184 +#define TK_ILLEGAL 185 #endif /**************** End token definitions ***************************************/ @@ -171579,6 +172923,9 @@ static void updateDeleteLimitError( ** sqlite3ParserARG_STORE Code to store %extra_argument into yypParser ** sqlite3ParserARG_FETCH Code to extract %extra_argument from yypParser ** sqlite3ParserCTX_* As sqlite3ParserARG_ except for %extra_context +** YYREALLOC Name of the realloc() function to use +** YYFREE Name of the free() function to use +** YYDYNSTACK True if stack space should be extended on heap ** YYERRORSYMBOL is the code number of the error symbol. If not ** defined, then do no error processing. ** YYNSTATE the combined number of states. @@ -171592,37 +172939,39 @@ static void updateDeleteLimitError( ** YY_NO_ACTION The yy_action[] code for no-op ** YY_MIN_REDUCE Minimum value for reduce actions ** YY_MAX_REDUCE Maximum value for reduce actions +** YY_MIN_DSTRCTR Minimum symbol value that has a destructor +** YY_MAX_DSTRCTR Maximum symbol value that has a destructor */ #ifndef INTERFACE # define INTERFACE 1 #endif /************* Begin control #defines *****************************************/ #define YYCODETYPE unsigned short int -#define YYNOCODE 319 +#define YYNOCODE 322 #define YYACTIONTYPE unsigned short int #define YYWILDCARD 101 #define sqlite3ParserTOKENTYPE Token typedef union { int yyinit; sqlite3ParserTOKENTYPE yy0; - TriggerStep* yy33; - Window* yy41; - Select* yy47; - SrcList* yy131; - struct TrigEvent yy180; - struct {int value; int mask;} yy231; - IdList* yy254; - u32 yy285; - ExprList* yy322; - Cte* yy385; - int yy394; - Upsert* yy444; - u8 yy516; - With* yy521; - const char* yy522; - Expr* yy528; - OnOrUsing yy561; - struct FrameBound yy595; + ExprList* yy14; + With* yy59; + Cte* yy67; + Upsert* yy122; + IdList* yy132; + int yy144; + const char* yy168; + SrcList* yy203; + Window* yy211; + OnOrUsing yy269; + struct TrigEvent yy286; + struct {int value; int mask;} yy383; + u32 yy391; + TriggerStep* yy427; + Expr* yy454; + u8 yy462; + struct FrameBound yy509; + Select* yy555; } YYMINORTYPE; #ifndef YYSTACKDEPTH #define YYSTACKDEPTH 100 @@ -171632,24 +172981,29 @@ typedef union { #define sqlite3ParserARG_PARAM #define sqlite3ParserARG_FETCH #define sqlite3ParserARG_STORE +#define YYREALLOC parserStackRealloc +#define YYFREE sqlite3_free +#define YYDYNSTACK 1 #define sqlite3ParserCTX_SDECL Parse *pParse; #define sqlite3ParserCTX_PDECL ,Parse *pParse #define sqlite3ParserCTX_PARAM ,pParse #define sqlite3ParserCTX_FETCH Parse *pParse=yypParser->pParse; #define sqlite3ParserCTX_STORE yypParser->pParse=pParse; #define YYFALLBACK 1 -#define YYNSTATE 579 -#define YYNRULE 405 -#define YYNRULE_WITH_ACTION 340 -#define YYNTOKEN 185 -#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 +#define YYNSTATE 583 +#define YYNRULE 409 +#define YYNRULE_WITH_ACTION 344 +#define YYNTOKEN 186 +#define YY_MAX_SHIFT 582 +#define YY_MIN_SHIFTREDUCE 845 +#define YY_MAX_SHIFTREDUCE 1253 +#define YY_ERROR_ACTION 1254 +#define YY_ACCEPT_ACTION 1255 +#define YY_NO_ACTION 1256 +#define YY_MIN_REDUCE 1257 +#define YY_MAX_REDUCE 1665 +#define YY_MIN_DSTRCTR 205 +#define YY_MAX_DSTRCTR 319 /************* End control #defines *******************************************/ #define YY_NLOOKAHEAD ((int)(sizeof(yy_lookahead)/sizeof(yy_lookahead[0]))) @@ -171665,6 +173019,22 @@ typedef union { # define yytestcase(X) #endif +/* Macro to determine if stack space has the ability to grow using +** heap memory. +*/ +#if YYSTACKDEPTH<=0 || YYDYNSTACK +# define YYGROWABLESTACK 1 +#else +# define YYGROWABLESTACK 0 +#endif + +/* Guarantee a minimum number of initial stack slots. +*/ +#if YYSTACKDEPTH<=0 +# undef YYSTACKDEPTH +# define YYSTACKDEPTH 2 /* Need a minimum stack size */ +#endif + /* Next are the tables used to determine what action to take based on the ** current state and lookahead token. These tables are used to implement @@ -171716,619 +173086,630 @@ typedef union { ** yy_default[] Default action for each state. ** *********** Begin parsing tables **********************************************/ -#define YY_ACTTAB_COUNT (2100) +#define YY_ACTTAB_COUNT (2142) static const YYACTIONTYPE yy_action[] = { - /* 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, + /* 0 */ 576, 128, 125, 232, 1622, 549, 576, 1290, 1281, 576, + /* 10 */ 328, 576, 1300, 212, 576, 128, 125, 232, 578, 412, + /* 20 */ 578, 391, 1542, 51, 51, 523, 405, 1293, 529, 51, + /* 30 */ 51, 983, 51, 51, 81, 81, 1107, 61, 61, 984, + /* 40 */ 1107, 1292, 380, 135, 136, 90, 1228, 1228, 1063, 1066, + /* 50 */ 1053, 1053, 133, 133, 134, 134, 134, 134, 1577, 412, + /* 60 */ 287, 287, 7, 287, 287, 422, 1050, 1050, 1064, 1067, + /* 70 */ 289, 556, 492, 573, 524, 561, 573, 497, 561, 482, + /* 80 */ 530, 262, 229, 135, 136, 90, 1228, 1228, 1063, 1066, + /* 90 */ 1053, 1053, 133, 133, 134, 134, 134, 134, 128, 125, + /* 100 */ 232, 1506, 132, 132, 132, 132, 131, 131, 130, 130, + /* 110 */ 130, 129, 126, 450, 1204, 1255, 1, 1, 582, 2, + /* 120 */ 1259, 1571, 420, 1582, 379, 320, 1174, 153, 1174, 1584, + /* 130 */ 412, 378, 1582, 543, 1341, 330, 111, 570, 570, 570, + /* 140 */ 293, 1054, 132, 132, 132, 132, 131, 131, 130, 130, + /* 150 */ 130, 129, 126, 450, 135, 136, 90, 1228, 1228, 1063, + /* 160 */ 1066, 1053, 1053, 133, 133, 134, 134, 134, 134, 287, + /* 170 */ 287, 1204, 1205, 1204, 255, 287, 287, 510, 507, 506, + /* 180 */ 137, 455, 573, 212, 561, 447, 446, 505, 573, 1616, + /* 190 */ 561, 134, 134, 134, 134, 127, 400, 243, 132, 132, + /* 200 */ 132, 132, 131, 131, 130, 130, 130, 129, 126, 450, + /* 210 */ 282, 471, 345, 132, 132, 132, 132, 131, 131, 130, + /* 220 */ 130, 130, 129, 126, 450, 574, 155, 936, 936, 454, + /* 230 */ 227, 521, 1236, 412, 1236, 134, 134, 134, 134, 132, + /* 240 */ 132, 132, 132, 131, 131, 130, 130, 130, 129, 126, + /* 250 */ 450, 130, 130, 130, 129, 126, 450, 135, 136, 90, + /* 260 */ 1228, 1228, 1063, 1066, 1053, 1053, 133, 133, 134, 134, + /* 270 */ 134, 134, 128, 125, 232, 450, 576, 412, 397, 1249, + /* 280 */ 180, 92, 93, 132, 132, 132, 132, 131, 131, 130, + /* 290 */ 130, 130, 129, 126, 450, 381, 387, 1204, 383, 81, + /* 300 */ 81, 135, 136, 90, 1228, 1228, 1063, 1066, 1053, 1053, + /* 310 */ 133, 133, 134, 134, 134, 134, 132, 132, 132, 132, + /* 320 */ 131, 131, 130, 130, 130, 129, 126, 450, 131, 131, + /* 330 */ 130, 130, 130, 129, 126, 450, 556, 1204, 302, 319, + /* 340 */ 567, 121, 568, 480, 4, 555, 1149, 1657, 1628, 1657, + /* 350 */ 45, 128, 125, 232, 1204, 1205, 1204, 1250, 571, 1169, + /* 360 */ 132, 132, 132, 132, 131, 131, 130, 130, 130, 129, + /* 370 */ 126, 450, 1169, 287, 287, 1169, 1019, 576, 422, 1019, + /* 380 */ 412, 451, 1602, 582, 2, 1259, 573, 44, 561, 95, + /* 390 */ 320, 110, 153, 565, 1204, 1205, 1204, 522, 522, 1341, + /* 400 */ 81, 81, 7, 44, 135, 136, 90, 1228, 1228, 1063, + /* 410 */ 1066, 1053, 1053, 133, 133, 134, 134, 134, 134, 295, + /* 420 */ 1149, 1658, 1040, 1658, 1204, 1147, 319, 567, 119, 119, + /* 430 */ 343, 466, 331, 343, 287, 287, 120, 556, 451, 577, + /* 440 */ 451, 1169, 1169, 1028, 319, 567, 438, 573, 210, 561, + /* 450 */ 1339, 1451, 546, 531, 1169, 1169, 1598, 1169, 1169, 416, + /* 460 */ 319, 567, 243, 132, 132, 132, 132, 131, 131, 130, + /* 470 */ 130, 130, 129, 126, 450, 1028, 1028, 1030, 1031, 35, + /* 480 */ 44, 1204, 1205, 1204, 472, 287, 287, 1328, 412, 1307, + /* 490 */ 372, 1595, 359, 225, 454, 1204, 195, 1328, 573, 1147, + /* 500 */ 561, 1333, 1333, 274, 576, 1188, 576, 340, 46, 196, + /* 510 */ 537, 217, 135, 136, 90, 1228, 1228, 1063, 1066, 1053, + /* 520 */ 1053, 133, 133, 134, 134, 134, 134, 19, 19, 19, + /* 530 */ 19, 412, 581, 1204, 1259, 511, 1204, 319, 567, 320, + /* 540 */ 944, 153, 425, 491, 430, 943, 1204, 488, 1341, 1450, + /* 550 */ 532, 1277, 1204, 1205, 1204, 135, 136, 90, 1228, 1228, + /* 560 */ 1063, 1066, 1053, 1053, 133, 133, 134, 134, 134, 134, + /* 570 */ 575, 132, 132, 132, 132, 131, 131, 130, 130, 130, + /* 580 */ 129, 126, 450, 287, 287, 528, 287, 287, 372, 1595, + /* 590 */ 1204, 1205, 1204, 1204, 1205, 1204, 573, 486, 561, 573, + /* 600 */ 889, 561, 412, 1204, 1205, 1204, 886, 40, 22, 22, + /* 610 */ 220, 243, 525, 1449, 132, 132, 132, 132, 131, 131, + /* 620 */ 130, 130, 130, 129, 126, 450, 135, 136, 90, 1228, + /* 630 */ 1228, 1063, 1066, 1053, 1053, 133, 133, 134, 134, 134, + /* 640 */ 134, 412, 180, 454, 1204, 879, 255, 287, 287, 510, + /* 650 */ 507, 506, 372, 1595, 1568, 1331, 1331, 576, 889, 505, + /* 660 */ 573, 44, 561, 559, 1207, 135, 136, 90, 1228, 1228, + /* 670 */ 1063, 1066, 1053, 1053, 133, 133, 134, 134, 134, 134, + /* 680 */ 81, 81, 422, 576, 377, 132, 132, 132, 132, 131, + /* 690 */ 131, 130, 130, 130, 129, 126, 450, 297, 287, 287, + /* 700 */ 460, 1204, 1205, 1204, 1204, 534, 19, 19, 448, 448, + /* 710 */ 448, 573, 412, 561, 230, 436, 1187, 535, 319, 567, + /* 720 */ 363, 432, 1207, 1435, 132, 132, 132, 132, 131, 131, + /* 730 */ 130, 130, 130, 129, 126, 450, 135, 136, 90, 1228, + /* 740 */ 1228, 1063, 1066, 1053, 1053, 133, 133, 134, 134, 134, + /* 750 */ 134, 412, 211, 949, 1169, 1041, 1110, 1110, 494, 547, + /* 760 */ 547, 1204, 1205, 1204, 7, 539, 1570, 1169, 376, 576, + /* 770 */ 1169, 5, 1204, 486, 3, 135, 136, 90, 1228, 1228, + /* 780 */ 1063, 1066, 1053, 1053, 133, 133, 134, 134, 134, 134, + /* 790 */ 576, 513, 19, 19, 427, 132, 132, 132, 132, 131, + /* 800 */ 131, 130, 130, 130, 129, 126, 450, 305, 1204, 433, + /* 810 */ 225, 1204, 385, 19, 19, 273, 290, 371, 516, 366, + /* 820 */ 515, 260, 412, 538, 1568, 549, 1024, 362, 437, 1204, + /* 830 */ 1205, 1204, 902, 1552, 132, 132, 132, 132, 131, 131, + /* 840 */ 130, 130, 130, 129, 126, 450, 135, 136, 90, 1228, + /* 850 */ 1228, 1063, 1066, 1053, 1053, 133, 133, 134, 134, 134, + /* 860 */ 134, 412, 1435, 514, 1281, 1204, 1205, 1204, 1204, 1205, + /* 870 */ 1204, 903, 48, 342, 1568, 1568, 1279, 1627, 1568, 911, + /* 880 */ 576, 129, 126, 450, 110, 135, 136, 90, 1228, 1228, + /* 890 */ 1063, 1066, 1053, 1053, 133, 133, 134, 134, 134, 134, + /* 900 */ 265, 576, 459, 19, 19, 132, 132, 132, 132, 131, + /* 910 */ 131, 130, 130, 130, 129, 126, 450, 1345, 204, 576, + /* 920 */ 459, 458, 50, 47, 19, 19, 49, 434, 1105, 573, + /* 930 */ 497, 561, 412, 428, 108, 1224, 1569, 1554, 376, 205, + /* 940 */ 550, 550, 81, 81, 132, 132, 132, 132, 131, 131, + /* 950 */ 130, 130, 130, 129, 126, 450, 135, 136, 90, 1228, + /* 960 */ 1228, 1063, 1066, 1053, 1053, 133, 133, 134, 134, 134, + /* 970 */ 134, 480, 576, 1204, 576, 1541, 412, 1435, 969, 315, + /* 980 */ 1659, 398, 284, 497, 969, 893, 1569, 1569, 376, 376, + /* 990 */ 1569, 461, 376, 1224, 459, 80, 80, 81, 81, 497, + /* 1000 */ 374, 114, 90, 1228, 1228, 1063, 1066, 1053, 1053, 133, + /* 1010 */ 133, 134, 134, 134, 134, 132, 132, 132, 132, 131, + /* 1020 */ 131, 130, 130, 130, 129, 126, 450, 1204, 1505, 576, + /* 1030 */ 1204, 1205, 1204, 1366, 316, 486, 281, 281, 497, 431, + /* 1040 */ 557, 288, 288, 402, 1340, 471, 345, 298, 429, 573, + /* 1050 */ 576, 561, 81, 81, 573, 374, 561, 971, 386, 132, + /* 1060 */ 132, 132, 132, 131, 131, 130, 130, 130, 129, 126, + /* 1070 */ 450, 231, 117, 81, 81, 287, 287, 231, 287, 287, + /* 1080 */ 576, 1511, 576, 1336, 1204, 1205, 1204, 139, 573, 556, + /* 1090 */ 561, 573, 412, 561, 441, 456, 969, 213, 558, 1511, + /* 1100 */ 1513, 1550, 969, 143, 143, 145, 145, 1368, 314, 478, + /* 1110 */ 444, 970, 412, 850, 851, 852, 135, 136, 90, 1228, + /* 1120 */ 1228, 1063, 1066, 1053, 1053, 133, 133, 134, 134, 134, + /* 1130 */ 134, 357, 412, 397, 1148, 304, 135, 136, 90, 1228, + /* 1140 */ 1228, 1063, 1066, 1053, 1053, 133, 133, 134, 134, 134, + /* 1150 */ 134, 1575, 323, 6, 862, 7, 135, 124, 90, 1228, + /* 1160 */ 1228, 1063, 1066, 1053, 1053, 133, 133, 134, 134, 134, + /* 1170 */ 134, 409, 408, 1511, 212, 132, 132, 132, 132, 131, + /* 1180 */ 131, 130, 130, 130, 129, 126, 450, 411, 118, 1204, + /* 1190 */ 116, 10, 352, 265, 355, 132, 132, 132, 132, 131, + /* 1200 */ 131, 130, 130, 130, 129, 126, 450, 576, 324, 306, + /* 1210 */ 576, 306, 1250, 469, 158, 132, 132, 132, 132, 131, + /* 1220 */ 131, 130, 130, 130, 129, 126, 450, 207, 1224, 1126, + /* 1230 */ 65, 65, 470, 66, 66, 412, 447, 446, 882, 531, + /* 1240 */ 335, 258, 257, 256, 1127, 1233, 1204, 1205, 1204, 327, + /* 1250 */ 1235, 874, 159, 576, 16, 480, 1085, 1040, 1234, 1128, + /* 1260 */ 136, 90, 1228, 1228, 1063, 1066, 1053, 1053, 133, 133, + /* 1270 */ 134, 134, 134, 134, 1029, 576, 81, 81, 1028, 1040, + /* 1280 */ 922, 576, 463, 1236, 576, 1236, 1224, 502, 107, 1435, + /* 1290 */ 923, 6, 576, 410, 1498, 882, 1029, 480, 21, 21, + /* 1300 */ 1028, 332, 1380, 334, 53, 53, 497, 81, 81, 874, + /* 1310 */ 1028, 1028, 1030, 445, 259, 19, 19, 533, 132, 132, + /* 1320 */ 132, 132, 131, 131, 130, 130, 130, 129, 126, 450, + /* 1330 */ 551, 301, 1028, 1028, 1030, 107, 532, 545, 121, 568, + /* 1340 */ 1188, 4, 1126, 1576, 449, 576, 462, 7, 1282, 418, + /* 1350 */ 462, 350, 1435, 576, 518, 571, 544, 1127, 121, 568, + /* 1360 */ 442, 4, 1188, 464, 533, 1180, 1223, 9, 67, 67, + /* 1370 */ 487, 576, 1128, 303, 410, 571, 54, 54, 451, 576, + /* 1380 */ 123, 944, 576, 417, 576, 333, 943, 1379, 576, 236, + /* 1390 */ 565, 576, 1574, 564, 68, 68, 7, 576, 451, 362, + /* 1400 */ 419, 182, 69, 69, 541, 70, 70, 71, 71, 540, + /* 1410 */ 565, 72, 72, 484, 55, 55, 473, 1180, 296, 1040, + /* 1420 */ 56, 56, 296, 493, 541, 119, 119, 410, 1573, 542, + /* 1430 */ 569, 418, 7, 120, 1244, 451, 577, 451, 465, 1040, + /* 1440 */ 1028, 576, 1557, 552, 476, 119, 119, 527, 259, 121, + /* 1450 */ 568, 240, 4, 120, 576, 451, 577, 451, 576, 477, + /* 1460 */ 1028, 576, 156, 576, 57, 57, 571, 576, 286, 229, + /* 1470 */ 410, 336, 1028, 1028, 1030, 1031, 35, 59, 59, 219, + /* 1480 */ 983, 60, 60, 220, 73, 73, 74, 74, 984, 451, + /* 1490 */ 75, 75, 1028, 1028, 1030, 1031, 35, 96, 216, 291, + /* 1500 */ 552, 565, 1188, 318, 395, 395, 394, 276, 392, 576, + /* 1510 */ 485, 859, 474, 1311, 410, 541, 576, 417, 1530, 1144, + /* 1520 */ 540, 399, 1188, 292, 237, 1153, 326, 38, 23, 576, + /* 1530 */ 1040, 576, 20, 20, 325, 299, 119, 119, 164, 76, + /* 1540 */ 76, 1529, 121, 568, 120, 4, 451, 577, 451, 203, + /* 1550 */ 576, 1028, 141, 141, 142, 142, 576, 322, 39, 571, + /* 1560 */ 341, 1021, 110, 264, 239, 901, 900, 423, 242, 908, + /* 1570 */ 909, 370, 173, 77, 77, 43, 479, 1310, 264, 62, + /* 1580 */ 62, 369, 451, 1028, 1028, 1030, 1031, 35, 1601, 1192, + /* 1590 */ 453, 1092, 238, 291, 565, 163, 1309, 110, 395, 395, + /* 1600 */ 394, 276, 392, 986, 987, 859, 481, 346, 264, 110, + /* 1610 */ 1032, 489, 576, 1188, 503, 1088, 261, 261, 237, 576, + /* 1620 */ 326, 121, 568, 1040, 4, 347, 1376, 413, 325, 119, + /* 1630 */ 119, 948, 319, 567, 351, 78, 78, 120, 571, 451, + /* 1640 */ 577, 451, 79, 79, 1028, 354, 356, 576, 360, 1092, + /* 1650 */ 110, 576, 974, 942, 264, 123, 457, 358, 239, 576, + /* 1660 */ 519, 451, 939, 1104, 123, 1104, 173, 576, 1032, 43, + /* 1670 */ 63, 63, 1324, 565, 168, 168, 1028, 1028, 1030, 1031, + /* 1680 */ 35, 576, 169, 169, 1308, 872, 238, 157, 1589, 576, + /* 1690 */ 86, 86, 365, 89, 568, 375, 4, 1103, 941, 1103, + /* 1700 */ 123, 576, 1040, 1389, 64, 64, 1188, 1434, 119, 119, + /* 1710 */ 571, 576, 82, 82, 563, 576, 120, 165, 451, 577, + /* 1720 */ 451, 413, 1362, 1028, 144, 144, 319, 567, 576, 1374, + /* 1730 */ 562, 498, 279, 451, 83, 83, 1439, 576, 166, 166, + /* 1740 */ 576, 1289, 554, 576, 1280, 565, 576, 12, 576, 1268, + /* 1750 */ 457, 146, 146, 1267, 576, 1028, 1028, 1030, 1031, 35, + /* 1760 */ 140, 140, 1269, 167, 167, 1609, 160, 160, 1359, 150, + /* 1770 */ 150, 149, 149, 311, 1040, 576, 312, 147, 147, 313, + /* 1780 */ 119, 119, 222, 235, 576, 1188, 396, 576, 120, 576, + /* 1790 */ 451, 577, 451, 1192, 453, 1028, 508, 291, 148, 148, + /* 1800 */ 1421, 1612, 395, 395, 394, 276, 392, 85, 85, 859, + /* 1810 */ 87, 87, 84, 84, 553, 576, 294, 576, 1426, 338, + /* 1820 */ 339, 1425, 237, 300, 326, 1416, 1409, 1028, 1028, 1030, + /* 1830 */ 1031, 35, 325, 344, 403, 483, 226, 1307, 52, 52, + /* 1840 */ 58, 58, 368, 1371, 1502, 566, 1501, 121, 568, 221, + /* 1850 */ 4, 208, 268, 209, 390, 1244, 1549, 1188, 1372, 1370, + /* 1860 */ 1369, 1547, 239, 184, 571, 233, 421, 1241, 95, 218, + /* 1870 */ 173, 1507, 193, 43, 91, 94, 178, 186, 467, 188, + /* 1880 */ 468, 1422, 13, 189, 190, 191, 501, 451, 245, 108, + /* 1890 */ 238, 401, 1428, 1427, 1430, 475, 404, 1496, 197, 565, + /* 1900 */ 14, 490, 249, 101, 1518, 496, 349, 280, 251, 201, + /* 1910 */ 353, 499, 252, 406, 1270, 253, 517, 1327, 1326, 435, + /* 1920 */ 1325, 1318, 103, 893, 1296, 413, 227, 407, 1040, 1626, + /* 1930 */ 319, 567, 1625, 1297, 119, 119, 439, 367, 1317, 1295, + /* 1940 */ 1624, 526, 120, 440, 451, 577, 451, 1594, 309, 1028, + /* 1950 */ 310, 373, 266, 267, 457, 1580, 1579, 443, 138, 1394, + /* 1960 */ 552, 1393, 11, 1483, 384, 115, 317, 1350, 109, 536, + /* 1970 */ 42, 579, 382, 214, 1349, 388, 1198, 389, 275, 277, + /* 1980 */ 278, 1028, 1028, 1030, 1031, 35, 580, 1265, 414, 1260, + /* 1990 */ 170, 415, 183, 1534, 1535, 1533, 171, 154, 307, 1532, + /* 2000 */ 846, 223, 224, 88, 452, 215, 172, 321, 234, 1102, + /* 2010 */ 152, 1188, 1100, 329, 185, 174, 1223, 925, 187, 241, + /* 2020 */ 337, 244, 1116, 192, 175, 176, 424, 426, 97, 194, + /* 2030 */ 98, 99, 100, 177, 1119, 1115, 246, 247, 161, 24, + /* 2040 */ 248, 348, 1238, 264, 1108, 250, 495, 199, 198, 15, + /* 2050 */ 861, 500, 369, 254, 504, 509, 512, 200, 102, 25, + /* 2060 */ 179, 361, 26, 364, 104, 891, 308, 162, 105, 904, + /* 2070 */ 520, 106, 1185, 1069, 1155, 17, 228, 27, 1154, 283, + /* 2080 */ 285, 263, 978, 202, 972, 123, 28, 1175, 29, 30, + /* 2090 */ 1179, 1171, 31, 1173, 1160, 41, 32, 206, 548, 33, + /* 2100 */ 110, 1178, 1083, 8, 112, 1070, 113, 1068, 1072, 34, + /* 2110 */ 1073, 560, 1125, 269, 1124, 270, 36, 18, 1194, 1033, + /* 2120 */ 873, 151, 122, 37, 393, 271, 272, 572, 181, 1193, + /* 2130 */ 1256, 1256, 1256, 935, 1256, 1256, 1256, 1256, 1256, 1256, + /* 2140 */ 1256, 1617, }; static const YYCODETYPE yy_lookahead[] = { - /* 0 */ 193, 193, 193, 274, 275, 276, 193, 274, 275, 276, - /* 10 */ 193, 223, 219, 225, 206, 210, 211, 212, 193, 19, - /* 20 */ 219, 233, 216, 216, 217, 216, 217, 193, 295, 216, - /* 30 */ 217, 31, 193, 216, 217, 193, 228, 213, 230, 39, - /* 40 */ 206, 216, 217, 43, 44, 45, 46, 47, 48, 49, - /* 50 */ 50, 51, 52, 53, 54, 55, 56, 57, 193, 19, - /* 60 */ 185, 186, 187, 188, 189, 190, 253, 274, 275, 276, - /* 70 */ 195, 193, 197, 193, 261, 274, 275, 276, 253, 204, - /* 80 */ 238, 204, 81, 43, 44, 45, 46, 47, 48, 49, - /* 90 */ 50, 51, 52, 53, 54, 55, 56, 57, 274, 275, - /* 100 */ 276, 262, 102, 103, 104, 105, 106, 107, 108, 109, - /* 110 */ 110, 111, 112, 113, 239, 240, 239, 240, 210, 211, - /* 120 */ 212, 314, 315, 314, 59, 316, 86, 252, 88, 252, - /* 130 */ 19, 314, 315, 256, 257, 113, 25, 72, 296, 138, - /* 140 */ 139, 266, 102, 103, 104, 105, 106, 107, 108, 109, + /* 0 */ 194, 276, 277, 278, 216, 194, 194, 217, 194, 194, + /* 10 */ 194, 194, 224, 194, 194, 276, 277, 278, 204, 19, + /* 20 */ 206, 202, 297, 217, 218, 205, 207, 217, 205, 217, + /* 30 */ 218, 31, 217, 218, 217, 218, 29, 217, 218, 39, + /* 40 */ 33, 217, 220, 43, 44, 45, 46, 47, 48, 49, + /* 50 */ 50, 51, 52, 53, 54, 55, 56, 57, 312, 19, + /* 60 */ 240, 241, 316, 240, 241, 194, 46, 47, 48, 49, + /* 70 */ 22, 254, 65, 253, 254, 255, 253, 194, 255, 194, + /* 80 */ 263, 258, 259, 43, 44, 45, 46, 47, 48, 49, + /* 90 */ 50, 51, 52, 53, 54, 55, 56, 57, 276, 277, + /* 100 */ 278, 285, 102, 103, 104, 105, 106, 107, 108, 109, + /* 110 */ 110, 111, 112, 113, 59, 186, 187, 188, 189, 190, + /* 120 */ 191, 310, 239, 317, 318, 196, 86, 198, 88, 317, + /* 130 */ 19, 319, 317, 318, 205, 264, 25, 211, 212, 213, + /* 140 */ 205, 121, 102, 103, 104, 105, 106, 107, 108, 109, /* 150 */ 110, 111, 112, 113, 43, 44, 45, 46, 47, 48, - /* 160 */ 49, 50, 51, 52, 53, 54, 55, 56, 57, 81, - /* 170 */ 292, 59, 292, 298, 108, 109, 110, 111, 112, 113, - /* 180 */ 69, 116, 117, 118, 72, 106, 107, 193, 111, 112, - /* 190 */ 113, 54, 55, 56, 57, 58, 102, 103, 104, 105, - /* 200 */ 106, 107, 108, 109, 110, 111, 112, 113, 120, 25, - /* 210 */ 216, 217, 145, 102, 103, 104, 105, 106, 107, 108, - /* 220 */ 109, 110, 111, 112, 113, 231, 138, 139, 116, 117, - /* 230 */ 118, 164, 153, 19, 155, 54, 55, 56, 57, 102, + /* 160 */ 49, 50, 51, 52, 53, 54, 55, 56, 57, 240, + /* 170 */ 241, 116, 117, 118, 119, 240, 241, 122, 123, 124, + /* 180 */ 69, 298, 253, 194, 255, 106, 107, 132, 253, 141, + /* 190 */ 255, 54, 55, 56, 57, 58, 207, 268, 102, 103, + /* 200 */ 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, + /* 210 */ 214, 128, 129, 102, 103, 104, 105, 106, 107, 108, + /* 220 */ 109, 110, 111, 112, 113, 134, 25, 136, 137, 300, + /* 230 */ 165, 166, 153, 19, 155, 54, 55, 56, 57, 102, /* 240 */ 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, - /* 250 */ 113, 128, 129, 46, 47, 48, 49, 43, 44, 45, + /* 250 */ 113, 108, 109, 110, 111, 112, 113, 43, 44, 45, /* 260 */ 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, - /* 270 */ 56, 57, 216, 193, 25, 59, 193, 19, 165, 166, - /* 280 */ 193, 67, 24, 102, 103, 104, 105, 106, 107, 108, - /* 290 */ 109, 110, 111, 112, 113, 73, 216, 217, 59, 216, - /* 300 */ 217, 43, 44, 45, 46, 47, 48, 49, 50, 51, + /* 270 */ 56, 57, 276, 277, 278, 113, 194, 19, 22, 23, + /* 280 */ 194, 67, 24, 102, 103, 104, 105, 106, 107, 108, + /* 290 */ 109, 110, 111, 112, 113, 220, 250, 59, 252, 217, + /* 300 */ 218, 43, 44, 45, 46, 47, 48, 49, 50, 51, /* 310 */ 52, 53, 54, 55, 56, 57, 102, 103, 104, 105, - /* 320 */ 106, 107, 108, 109, 110, 111, 112, 113, 121, 145, - /* 330 */ 59, 193, 116, 117, 118, 119, 273, 204, 122, 123, - /* 340 */ 124, 19, 20, 134, 22, 136, 137, 19, 132, 127, - /* 350 */ 128, 129, 24, 22, 23, 116, 117, 118, 36, 193, + /* 320 */ 106, 107, 108, 109, 110, 111, 112, 113, 106, 107, + /* 330 */ 108, 109, 110, 111, 112, 113, 254, 59, 205, 138, + /* 340 */ 139, 19, 20, 194, 22, 263, 22, 23, 231, 25, + /* 350 */ 72, 276, 277, 278, 116, 117, 118, 101, 36, 76, /* 360 */ 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, - /* 370 */ 112, 113, 239, 240, 311, 312, 215, 106, 107, 241, - /* 380 */ 19, 59, 216, 217, 223, 252, 115, 116, 117, 118, - /* 390 */ 151, 120, 26, 71, 193, 308, 309, 193, 149, 128, - /* 400 */ 313, 216, 269, 81, 43, 44, 45, 46, 47, 48, - /* 410 */ 49, 50, 51, 52, 53, 54, 55, 56, 57, 253, - /* 420 */ 216, 217, 100, 95, 153, 59, 155, 261, 106, 107, - /* 430 */ 25, 193, 101, 193, 193, 231, 114, 25, 116, 117, - /* 440 */ 118, 113, 304, 121, 193, 204, 59, 119, 120, 121, - /* 450 */ 122, 123, 124, 125, 216, 217, 193, 216, 217, 131, - /* 460 */ 138, 139, 230, 102, 103, 104, 105, 106, 107, 108, + /* 370 */ 112, 113, 89, 240, 241, 92, 73, 194, 194, 73, + /* 380 */ 19, 59, 188, 189, 190, 191, 253, 81, 255, 151, + /* 390 */ 196, 25, 198, 71, 116, 117, 118, 311, 312, 205, + /* 400 */ 217, 218, 316, 81, 43, 44, 45, 46, 47, 48, + /* 410 */ 49, 50, 51, 52, 53, 54, 55, 56, 57, 270, + /* 420 */ 22, 23, 100, 25, 59, 101, 138, 139, 106, 107, + /* 430 */ 127, 128, 129, 127, 240, 241, 114, 254, 116, 117, + /* 440 */ 118, 76, 76, 121, 138, 139, 263, 253, 264, 255, + /* 450 */ 205, 275, 87, 19, 89, 89, 194, 92, 92, 199, + /* 460 */ 138, 139, 268, 102, 103, 104, 105, 106, 107, 108, /* 470 */ 109, 110, 111, 112, 113, 153, 154, 155, 156, 157, - /* 480 */ 239, 240, 116, 117, 118, 76, 193, 23, 19, 25, - /* 490 */ 22, 253, 23, 252, 253, 108, 87, 204, 89, 261, - /* 500 */ 198, 92, 261, 116, 117, 118, 193, 306, 307, 216, - /* 510 */ 217, 150, 43, 44, 45, 46, 47, 48, 49, 50, - /* 520 */ 51, 52, 53, 54, 55, 56, 57, 59, 193, 216, - /* 530 */ 217, 19, 239, 240, 283, 23, 106, 107, 108, 109, - /* 540 */ 110, 111, 112, 113, 73, 252, 253, 142, 308, 309, - /* 550 */ 138, 139, 81, 313, 145, 43, 44, 45, 46, 47, + /* 480 */ 81, 116, 117, 118, 129, 240, 241, 224, 19, 226, + /* 490 */ 314, 315, 23, 25, 300, 59, 22, 234, 253, 101, + /* 500 */ 255, 236, 237, 26, 194, 183, 194, 152, 72, 22, + /* 510 */ 145, 150, 43, 44, 45, 46, 47, 48, 49, 50, + /* 520 */ 51, 52, 53, 54, 55, 56, 57, 217, 218, 217, + /* 530 */ 218, 19, 189, 59, 191, 23, 59, 138, 139, 196, + /* 540 */ 135, 198, 232, 283, 232, 140, 59, 287, 205, 275, + /* 550 */ 116, 205, 116, 117, 118, 43, 44, 45, 46, 47, /* 560 */ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, - /* 570 */ 307, 102, 103, 104, 105, 106, 107, 108, 109, 110, - /* 580 */ 111, 112, 113, 281, 116, 117, 118, 285, 23, 193, - /* 590 */ 25, 119, 59, 193, 122, 123, 124, 59, 127, 203, - /* 600 */ 59, 205, 19, 268, 132, 25, 23, 22, 193, 138, - /* 610 */ 139, 249, 204, 251, 102, 103, 104, 105, 106, 107, + /* 570 */ 194, 102, 103, 104, 105, 106, 107, 108, 109, 110, + /* 580 */ 111, 112, 113, 240, 241, 194, 240, 241, 314, 315, + /* 590 */ 116, 117, 118, 116, 117, 118, 253, 194, 255, 253, + /* 600 */ 59, 255, 19, 116, 117, 118, 23, 22, 217, 218, + /* 610 */ 142, 268, 205, 275, 102, 103, 104, 105, 106, 107, /* 620 */ 108, 109, 110, 111, 112, 113, 43, 44, 45, 46, /* 630 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, - /* 640 */ 57, 19, 22, 23, 59, 23, 25, 239, 240, 116, - /* 650 */ 117, 118, 193, 11, 116, 117, 118, 116, 117, 118, - /* 660 */ 252, 269, 22, 193, 15, 43, 44, 45, 46, 47, + /* 640 */ 57, 19, 194, 300, 59, 23, 119, 240, 241, 122, + /* 650 */ 123, 124, 314, 315, 194, 236, 237, 194, 117, 132, + /* 660 */ 253, 81, 255, 205, 59, 43, 44, 45, 46, 47, /* 670 */ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, - /* 680 */ 273, 143, 193, 118, 143, 102, 103, 104, 105, 106, - /* 690 */ 107, 108, 109, 110, 111, 112, 113, 76, 118, 59, - /* 700 */ 241, 116, 117, 118, 304, 216, 217, 292, 143, 60, - /* 710 */ 89, 241, 19, 92, 193, 193, 23, 22, 311, 312, - /* 720 */ 231, 101, 22, 143, 102, 103, 104, 105, 106, 107, + /* 680 */ 217, 218, 194, 194, 194, 102, 103, 104, 105, 106, + /* 690 */ 107, 108, 109, 110, 111, 112, 113, 294, 240, 241, + /* 700 */ 120, 116, 117, 118, 59, 194, 217, 218, 211, 212, + /* 710 */ 213, 253, 19, 255, 194, 19, 23, 254, 138, 139, + /* 720 */ 24, 232, 117, 194, 102, 103, 104, 105, 106, 107, /* 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, 193, 206, 127, 128, 129, 193, - /* 770 */ 128, 129, 235, 236, 304, 43, 44, 45, 46, 47, + /* 750 */ 57, 19, 264, 108, 76, 23, 127, 128, 129, 311, + /* 760 */ 312, 116, 117, 118, 316, 87, 306, 89, 308, 194, + /* 770 */ 92, 22, 59, 194, 22, 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, - /* 800 */ 107, 108, 109, 110, 111, 112, 113, 231, 193, 193, - /* 810 */ 193, 116, 117, 118, 216, 217, 116, 117, 118, 226, - /* 820 */ 80, 193, 19, 235, 236, 304, 23, 211, 212, 231, - /* 830 */ 204, 216, 217, 205, 102, 103, 104, 105, 106, 107, + /* 790 */ 194, 95, 217, 218, 265, 102, 103, 104, 105, 106, + /* 800 */ 107, 108, 109, 110, 111, 112, 113, 232, 59, 113, + /* 810 */ 25, 59, 194, 217, 218, 119, 120, 121, 122, 123, + /* 820 */ 124, 125, 19, 145, 194, 194, 23, 131, 232, 116, + /* 830 */ 117, 118, 35, 194, 102, 103, 104, 105, 106, 107, /* 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, 244, 106, 107, 193, 89, 252, 193, - /* 880 */ 92, 59, 252, 254, 252, 43, 44, 45, 46, 47, + /* 860 */ 57, 19, 194, 66, 194, 116, 117, 118, 116, 117, + /* 870 */ 118, 74, 242, 294, 194, 194, 206, 23, 194, 25, + /* 880 */ 194, 111, 112, 113, 25, 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, 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, 273, 102, 103, 104, 105, 106, 107, + /* 900 */ 24, 194, 194, 217, 218, 102, 103, 104, 105, 106, + /* 910 */ 107, 108, 109, 110, 111, 112, 113, 241, 232, 194, + /* 920 */ 212, 213, 242, 242, 217, 218, 242, 130, 11, 253, + /* 930 */ 194, 255, 19, 265, 149, 59, 306, 194, 308, 232, + /* 940 */ 309, 310, 217, 218, 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 */ 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, + /* 970 */ 57, 194, 194, 59, 194, 239, 19, 194, 25, 254, + /* 980 */ 303, 304, 23, 194, 25, 126, 306, 306, 308, 308, + /* 990 */ 306, 271, 308, 117, 286, 217, 218, 217, 218, 194, + /* 1000 */ 194, 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 */ 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, + /* 1020 */ 107, 108, 109, 110, 111, 112, 113, 59, 239, 194, + /* 1030 */ 116, 117, 118, 260, 254, 194, 240, 241, 194, 233, + /* 1040 */ 205, 240, 241, 205, 239, 128, 129, 270, 265, 253, + /* 1050 */ 194, 255, 217, 218, 253, 194, 255, 143, 280, 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, 240, - /* 1090 */ 238, 231, 19, 239, 240, 252, 22, 24, 211, 212, - /* 1100 */ 263, 252, 216, 217, 216, 217, 252, 153, 154, 155, - /* 1110 */ 253, 193, 19, 144, 213, 268, 43, 44, 45, 46, + /* 1070 */ 113, 118, 159, 217, 218, 240, 241, 118, 240, 241, + /* 1080 */ 194, 194, 194, 239, 116, 117, 118, 22, 253, 254, + /* 1090 */ 255, 253, 19, 255, 233, 194, 143, 24, 263, 212, + /* 1100 */ 213, 194, 143, 217, 218, 217, 218, 261, 262, 271, + /* 1110 */ 254, 143, 19, 7, 8, 9, 43, 44, 45, 46, /* 1120 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, - /* 1130 */ 57, 193, 19, 59, 216, 217, 43, 44, 45, 46, + /* 1130 */ 57, 16, 19, 22, 23, 294, 43, 44, 45, 46, /* 1140 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, - /* 1150 */ 57, 193, 19, 24, 216, 217, 43, 44, 45, 46, + /* 1150 */ 57, 312, 194, 214, 21, 316, 43, 44, 45, 46, /* 1160 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, - /* 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, + /* 1170 */ 57, 106, 107, 286, 194, 102, 103, 104, 105, 106, + /* 1180 */ 107, 108, 109, 110, 111, 112, 113, 207, 158, 59, + /* 1190 */ 160, 22, 77, 24, 79, 102, 103, 104, 105, 106, + /* 1200 */ 107, 108, 109, 110, 111, 112, 113, 194, 194, 229, + /* 1210 */ 194, 231, 101, 80, 22, 102, 103, 104, 105, 106, + /* 1220 */ 107, 108, 109, 110, 111, 112, 113, 288, 59, 12, + /* 1230 */ 217, 218, 293, 217, 218, 19, 106, 107, 59, 19, + /* 1240 */ 16, 127, 128, 129, 27, 115, 116, 117, 118, 194, + /* 1250 */ 120, 59, 22, 194, 24, 194, 123, 100, 128, 42, /* 1260 */ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, - /* 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, + /* 1270 */ 54, 55, 56, 57, 117, 194, 217, 218, 121, 100, + /* 1280 */ 63, 194, 245, 153, 194, 155, 117, 19, 115, 194, + /* 1290 */ 73, 214, 194, 256, 161, 116, 117, 194, 217, 218, + /* 1300 */ 121, 77, 194, 79, 217, 218, 194, 217, 218, 117, + /* 1310 */ 153, 154, 155, 254, 46, 217, 218, 144, 102, 103, /* 1320 */ 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, - /* 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, 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, 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, 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, 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, - /* 2130 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, - /* 2140 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, - /* 2150 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, - /* 2160 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, - /* 2170 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, - /* 2180 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, - /* 2190 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, - /* 2200 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, - /* 2210 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, - /* 2220 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, - /* 2230 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, - /* 2240 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, - /* 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, 319, 319, 319, 319, + /* 1330 */ 232, 270, 153, 154, 155, 115, 116, 66, 19, 20, + /* 1340 */ 183, 22, 12, 312, 254, 194, 262, 316, 209, 210, + /* 1350 */ 266, 239, 194, 194, 108, 36, 85, 27, 19, 20, + /* 1360 */ 265, 22, 183, 245, 144, 94, 25, 48, 217, 218, + /* 1370 */ 293, 194, 42, 270, 256, 36, 217, 218, 59, 194, + /* 1380 */ 25, 135, 194, 115, 194, 161, 140, 194, 194, 15, + /* 1390 */ 71, 194, 312, 63, 217, 218, 316, 194, 59, 131, + /* 1400 */ 301, 302, 217, 218, 85, 217, 218, 217, 218, 90, + /* 1410 */ 71, 217, 218, 19, 217, 218, 245, 146, 262, 100, + /* 1420 */ 217, 218, 266, 265, 85, 106, 107, 256, 312, 90, + /* 1430 */ 209, 210, 316, 114, 60, 116, 117, 118, 194, 100, + /* 1440 */ 121, 194, 194, 145, 115, 106, 107, 19, 46, 19, + /* 1450 */ 20, 24, 22, 114, 194, 116, 117, 118, 194, 245, + /* 1460 */ 121, 194, 164, 194, 217, 218, 36, 194, 258, 259, + /* 1470 */ 256, 194, 153, 154, 155, 156, 157, 217, 218, 150, + /* 1480 */ 31, 217, 218, 142, 217, 218, 217, 218, 39, 59, + /* 1490 */ 217, 218, 153, 154, 155, 156, 157, 149, 150, 5, + /* 1500 */ 145, 71, 183, 245, 10, 11, 12, 13, 14, 194, + /* 1510 */ 116, 17, 129, 227, 256, 85, 194, 115, 194, 23, + /* 1520 */ 90, 25, 183, 99, 30, 97, 32, 22, 22, 194, + /* 1530 */ 100, 194, 217, 218, 40, 152, 106, 107, 23, 217, + /* 1540 */ 218, 194, 19, 20, 114, 22, 116, 117, 118, 257, + /* 1550 */ 194, 121, 217, 218, 217, 218, 194, 133, 53, 36, + /* 1560 */ 23, 23, 25, 25, 70, 120, 121, 61, 141, 7, + /* 1570 */ 8, 121, 78, 217, 218, 81, 23, 227, 25, 217, + /* 1580 */ 218, 131, 59, 153, 154, 155, 156, 157, 0, 1, + /* 1590 */ 2, 59, 98, 5, 71, 23, 227, 25, 10, 11, + /* 1600 */ 12, 13, 14, 83, 84, 17, 23, 23, 25, 25, + /* 1610 */ 59, 194, 194, 183, 23, 23, 25, 25, 30, 194, + /* 1620 */ 32, 19, 20, 100, 22, 194, 194, 133, 40, 106, + /* 1630 */ 107, 108, 138, 139, 194, 217, 218, 114, 36, 116, + /* 1640 */ 117, 118, 217, 218, 121, 194, 194, 194, 23, 117, + /* 1650 */ 25, 194, 23, 23, 25, 25, 162, 194, 70, 194, + /* 1660 */ 145, 59, 23, 153, 25, 155, 78, 194, 117, 81, + /* 1670 */ 217, 218, 194, 71, 217, 218, 153, 154, 155, 156, + /* 1680 */ 157, 194, 217, 218, 194, 23, 98, 25, 321, 194, + /* 1690 */ 217, 218, 194, 19, 20, 194, 22, 153, 23, 155, + /* 1700 */ 25, 194, 100, 194, 217, 218, 183, 194, 106, 107, + /* 1710 */ 36, 194, 217, 218, 237, 194, 114, 243, 116, 117, + /* 1720 */ 118, 133, 194, 121, 217, 218, 138, 139, 194, 194, + /* 1730 */ 194, 290, 289, 59, 217, 218, 194, 194, 217, 218, + /* 1740 */ 194, 194, 140, 194, 194, 71, 194, 244, 194, 194, + /* 1750 */ 162, 217, 218, 194, 194, 153, 154, 155, 156, 157, + /* 1760 */ 217, 218, 194, 217, 218, 194, 217, 218, 257, 217, + /* 1770 */ 218, 217, 218, 257, 100, 194, 257, 217, 218, 257, + /* 1780 */ 106, 107, 215, 299, 194, 183, 192, 194, 114, 194, + /* 1790 */ 116, 117, 118, 1, 2, 121, 221, 5, 217, 218, + /* 1800 */ 273, 197, 10, 11, 12, 13, 14, 217, 218, 17, + /* 1810 */ 217, 218, 217, 218, 140, 194, 246, 194, 273, 295, + /* 1820 */ 247, 273, 30, 247, 32, 269, 269, 153, 154, 155, + /* 1830 */ 156, 157, 40, 246, 273, 295, 230, 226, 217, 218, + /* 1840 */ 217, 218, 220, 261, 220, 282, 220, 19, 20, 244, + /* 1850 */ 22, 250, 141, 250, 246, 60, 201, 183, 261, 261, + /* 1860 */ 261, 201, 70, 299, 36, 299, 201, 38, 151, 150, + /* 1870 */ 78, 285, 22, 81, 296, 296, 43, 235, 18, 238, + /* 1880 */ 201, 274, 272, 238, 238, 238, 18, 59, 200, 149, + /* 1890 */ 98, 247, 274, 274, 235, 247, 247, 247, 235, 71, + /* 1900 */ 272, 201, 200, 158, 292, 62, 291, 201, 200, 22, + /* 1910 */ 201, 222, 200, 222, 201, 200, 115, 219, 219, 64, + /* 1920 */ 219, 228, 22, 126, 221, 133, 165, 222, 100, 225, + /* 1930 */ 138, 139, 225, 219, 106, 107, 24, 219, 228, 219, + /* 1940 */ 219, 307, 114, 113, 116, 117, 118, 315, 284, 121, + /* 1950 */ 284, 222, 201, 91, 162, 320, 320, 82, 148, 267, + /* 1960 */ 145, 267, 22, 279, 201, 158, 281, 251, 147, 146, + /* 1970 */ 25, 203, 250, 249, 251, 248, 13, 247, 195, 195, + /* 1980 */ 6, 153, 154, 155, 156, 157, 193, 193, 305, 193, + /* 1990 */ 208, 305, 302, 214, 214, 214, 208, 223, 223, 214, + /* 2000 */ 4, 215, 215, 214, 3, 22, 208, 163, 15, 23, + /* 2010 */ 16, 183, 23, 139, 151, 130, 25, 20, 142, 24, + /* 2020 */ 16, 144, 1, 142, 130, 130, 61, 37, 53, 151, + /* 2030 */ 53, 53, 53, 130, 116, 1, 34, 141, 5, 22, + /* 2040 */ 115, 161, 75, 25, 68, 141, 41, 115, 68, 24, + /* 2050 */ 20, 19, 131, 125, 67, 67, 96, 22, 22, 22, + /* 2060 */ 37, 23, 22, 24, 22, 59, 67, 23, 149, 28, + /* 2070 */ 22, 25, 23, 23, 23, 22, 141, 34, 97, 23, + /* 2080 */ 23, 34, 116, 22, 143, 25, 34, 75, 34, 34, + /* 2090 */ 75, 88, 34, 86, 23, 22, 34, 25, 24, 34, + /* 2100 */ 25, 93, 23, 44, 142, 23, 142, 23, 23, 22, + /* 2110 */ 11, 25, 23, 25, 23, 22, 22, 22, 1, 23, + /* 2120 */ 23, 23, 22, 22, 15, 141, 141, 25, 25, 1, + /* 2130 */ 322, 322, 322, 135, 322, 322, 322, 322, 322, 322, + /* 2140 */ 322, 141, 322, 322, 322, 322, 322, 322, 322, 322, + /* 2150 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, + /* 2160 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, + /* 2170 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, + /* 2180 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, + /* 2190 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, + /* 2200 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, + /* 2210 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, + /* 2220 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, + /* 2230 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, + /* 2240 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, + /* 2250 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, + /* 2260 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, + /* 2270 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, + /* 2280 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, + /* 2290 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, + /* 2300 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, + /* 2310 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, + /* 2320 */ 322, 322, 322, 322, 322, 322, 322, 322, }; -#define YY_SHIFT_COUNT (578) +#define YY_SHIFT_COUNT (582) #define YY_SHIFT_MIN (0) -#define YY_SHIFT_MAX (2088) +#define YY_SHIFT_MAX (2128) 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 */ 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, 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 */ 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, + /* 0 */ 1792, 1588, 1494, 322, 322, 399, 306, 1319, 1339, 1430, + /* 10 */ 1828, 1828, 1828, 580, 399, 399, 399, 399, 399, 0, + /* 20 */ 0, 214, 1093, 1828, 1828, 1828, 1828, 1828, 1828, 1828, + /* 30 */ 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1130, 1130, + /* 40 */ 365, 365, 55, 278, 436, 713, 713, 201, 201, 201, + /* 50 */ 201, 40, 111, 258, 361, 469, 512, 583, 622, 693, + /* 60 */ 732, 803, 842, 913, 1073, 1093, 1093, 1093, 1093, 1093, + /* 70 */ 1093, 1093, 1093, 1093, 1093, 1093, 1093, 1093, 1093, 1093, + /* 80 */ 1093, 1093, 1093, 1113, 1093, 1216, 957, 957, 1523, 1602, + /* 90 */ 1674, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, + /* 100 */ 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, + /* 110 */ 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, + /* 120 */ 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, + /* 130 */ 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, + /* 140 */ 137, 181, 181, 181, 181, 181, 181, 181, 96, 222, + /* 150 */ 143, 477, 713, 1133, 1268, 713, 713, 79, 79, 713, + /* 160 */ 770, 83, 65, 65, 65, 288, 162, 162, 2142, 2142, + /* 170 */ 696, 696, 696, 238, 474, 474, 474, 474, 1217, 1217, + /* 180 */ 678, 477, 324, 398, 713, 713, 713, 713, 713, 713, + /* 190 */ 713, 713, 713, 713, 713, 713, 713, 713, 713, 713, + /* 200 */ 713, 713, 713, 1220, 366, 366, 713, 917, 283, 283, + /* 210 */ 434, 434, 605, 605, 1298, 2142, 2142, 2142, 2142, 2142, + /* 220 */ 2142, 2142, 1179, 1157, 1157, 487, 527, 585, 645, 749, + /* 230 */ 914, 968, 752, 713, 713, 713, 713, 713, 713, 713, + /* 240 */ 713, 713, 713, 303, 713, 713, 713, 713, 713, 713, + /* 250 */ 713, 713, 713, 713, 713, 713, 797, 797, 797, 713, + /* 260 */ 713, 713, 959, 713, 713, 713, 1169, 1271, 713, 713, + /* 270 */ 1330, 713, 713, 713, 713, 713, 713, 713, 713, 629, + /* 280 */ 7, 91, 876, 876, 876, 876, 953, 91, 91, 1246, + /* 290 */ 1065, 1106, 1374, 1329, 1348, 468, 1348, 1394, 785, 1329, + /* 300 */ 1329, 785, 1329, 468, 1394, 859, 854, 1402, 1449, 1449, + /* 310 */ 1449, 1173, 1173, 1173, 1173, 1355, 1355, 1030, 1341, 405, + /* 320 */ 1230, 1795, 1795, 1711, 1711, 1829, 1829, 1711, 1717, 1719, + /* 330 */ 1850, 1833, 1860, 1860, 1860, 1860, 1711, 1868, 1740, 1719, + /* 340 */ 1719, 1740, 1850, 1833, 1740, 1833, 1740, 1711, 1868, 1745, + /* 350 */ 1843, 1711, 1868, 1887, 1711, 1868, 1711, 1868, 1887, 1801, + /* 360 */ 1801, 1801, 1855, 1900, 1900, 1887, 1801, 1797, 1801, 1855, + /* 370 */ 1801, 1801, 1761, 1912, 1830, 1830, 1887, 1711, 1862, 1862, + /* 380 */ 1875, 1875, 1810, 1815, 1940, 1711, 1807, 1810, 1821, 1823, + /* 390 */ 1740, 1945, 1963, 1963, 1974, 1974, 1974, 2142, 2142, 2142, + /* 400 */ 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, + /* 410 */ 2142, 2142, 20, 1224, 256, 1111, 1115, 1114, 1192, 1496, + /* 420 */ 1424, 1505, 1427, 355, 1383, 1537, 1506, 1538, 1553, 1583, + /* 430 */ 1584, 1591, 1625, 541, 1445, 1562, 1450, 1572, 1515, 1428, + /* 440 */ 1532, 1592, 1629, 1520, 1630, 1639, 1510, 1544, 1662, 1675, + /* 450 */ 1551, 48, 1996, 2001, 1983, 1844, 1993, 1994, 1986, 1989, + /* 460 */ 1874, 1863, 1885, 1991, 1991, 1995, 1876, 1997, 1877, 2004, + /* 470 */ 2021, 1881, 1894, 1991, 1895, 1965, 1990, 1991, 1878, 1975, + /* 480 */ 1977, 1978, 1979, 1903, 1918, 2002, 1896, 2034, 2033, 2017, + /* 490 */ 1925, 1880, 1976, 2018, 1980, 1967, 2005, 1904, 1932, 2025, + /* 500 */ 2030, 2032, 1921, 1928, 2035, 1987, 2036, 2037, 2038, 2040, + /* 510 */ 1988, 2006, 2039, 1960, 2041, 2042, 1999, 2023, 2044, 2043, + /* 520 */ 1919, 2048, 2049, 2050, 2046, 2051, 2053, 1981, 1935, 2056, + /* 530 */ 2057, 1966, 2047, 2061, 1941, 2060, 2052, 2054, 2055, 2058, + /* 540 */ 2003, 2012, 2007, 2059, 2015, 2008, 2062, 2071, 2073, 2074, + /* 550 */ 2072, 2075, 2065, 1962, 1964, 2079, 2060, 2082, 2084, 2085, + /* 560 */ 2087, 2086, 2089, 2088, 2091, 2093, 2099, 2094, 2095, 2096, + /* 570 */ 2097, 2100, 2101, 2102, 1998, 1984, 1985, 2000, 2103, 2098, + /* 580 */ 2109, 2117, 2128, }; -#define YY_REDUCE_COUNT (410) -#define YY_REDUCE_MIN (-271) -#define YY_REDUCE_MAX (1753) +#define YY_REDUCE_COUNT (411) +#define YY_REDUCE_MIN (-275) +#define YY_REDUCE_MAX (1798) 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, 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, -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, + /* 0 */ -71, 194, 343, 835, -180, -177, 838, -194, -188, -185, + /* 10 */ -183, 82, 183, -65, 133, 245, 346, 407, 458, -178, + /* 20 */ 75, -275, -4, 310, 312, 489, 575, 596, 463, 686, + /* 30 */ 707, 725, 780, 1098, 856, 778, 1059, 1090, 708, 887, + /* 40 */ 86, 448, 980, 630, 680, 681, 684, 796, 801, 796, + /* 50 */ 801, -261, -261, -261, -261, -261, -261, -261, -261, -261, + /* 60 */ -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, + /* 70 */ -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, + /* 80 */ -261, -261, -261, -261, -261, -261, -261, -261, 391, 886, + /* 90 */ 888, 1013, 1016, 1081, 1087, 1151, 1159, 1177, 1185, 1188, + /* 100 */ 1190, 1194, 1197, 1203, 1247, 1260, 1264, 1267, 1269, 1273, + /* 110 */ 1315, 1322, 1335, 1337, 1356, 1362, 1418, 1425, 1453, 1457, + /* 120 */ 1465, 1473, 1487, 1495, 1507, 1517, 1521, 1534, 1543, 1546, + /* 130 */ 1549, 1552, 1554, 1560, 1581, 1590, 1593, 1595, 1621, 1623, + /* 140 */ -261, -261, -261, -261, -261, -261, -261, -261, -261, -261, + /* 150 */ -261, -186, -117, 260, 263, 460, 631, -74, 497, -181, + /* 160 */ -261, 939, 176, 274, 338, 676, -261, -261, -261, -261, + /* 170 */ -212, -212, -212, -184, 149, 777, 1061, 1103, 265, 419, + /* 180 */ -254, 670, 677, 677, -11, -129, 184, 488, 736, 789, + /* 190 */ 805, 844, 403, 529, 579, 668, 783, 841, 1158, 1112, + /* 200 */ 806, 861, 1095, 846, 839, 1031, -189, 1077, 1080, 1116, + /* 210 */ 1084, 1156, 1139, 1221, 46, 1099, 1037, 1118, 1171, 1214, + /* 220 */ 1210, 1258, -210, -190, -176, -115, 117, 262, 376, 490, + /* 230 */ 511, 520, 618, 639, 743, 901, 907, 958, 1014, 1055, + /* 240 */ 1108, 1193, 1244, 720, 1248, 1277, 1324, 1347, 1417, 1431, + /* 250 */ 1432, 1440, 1451, 1452, 1463, 1478, 1286, 1350, 1369, 1490, + /* 260 */ 1498, 1501, 773, 1509, 1513, 1528, 1292, 1367, 1535, 1536, + /* 270 */ 1477, 1542, 376, 1547, 1550, 1555, 1559, 1568, 1571, 1441, + /* 280 */ 1443, 1474, 1511, 1516, 1519, 1522, 773, 1474, 1474, 1503, + /* 290 */ 1567, 1594, 1484, 1527, 1556, 1570, 1557, 1524, 1573, 1545, + /* 300 */ 1548, 1576, 1561, 1587, 1540, 1575, 1606, 1611, 1622, 1624, + /* 310 */ 1626, 1582, 1597, 1598, 1599, 1601, 1603, 1563, 1608, 1605, + /* 320 */ 1604, 1564, 1566, 1655, 1660, 1578, 1579, 1665, 1586, 1607, + /* 330 */ 1610, 1642, 1641, 1645, 1646, 1647, 1679, 1688, 1644, 1618, + /* 340 */ 1619, 1648, 1628, 1659, 1649, 1663, 1650, 1700, 1702, 1612, + /* 350 */ 1615, 1706, 1708, 1689, 1709, 1712, 1713, 1715, 1691, 1698, + /* 360 */ 1699, 1701, 1693, 1704, 1707, 1705, 1714, 1703, 1718, 1710, + /* 370 */ 1720, 1721, 1632, 1634, 1664, 1666, 1729, 1751, 1635, 1636, + /* 380 */ 1692, 1694, 1716, 1722, 1684, 1763, 1685, 1723, 1724, 1727, + /* 390 */ 1730, 1768, 1783, 1784, 1793, 1794, 1796, 1683, 1686, 1690, + /* 400 */ 1782, 1779, 1780, 1781, 1785, 1788, 1774, 1775, 1786, 1787, + /* 410 */ 1789, 1798, }; static const YYACTIONTYPE yy_default[] = { - /* 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, + /* 0 */ 1663, 1663, 1663, 1491, 1254, 1367, 1254, 1254, 1254, 1254, + /* 10 */ 1491, 1491, 1491, 1254, 1254, 1254, 1254, 1254, 1254, 1397, + /* 20 */ 1397, 1544, 1287, 1254, 1254, 1254, 1254, 1254, 1254, 1254, + /* 30 */ 1254, 1254, 1254, 1254, 1254, 1490, 1254, 1254, 1254, 1254, + /* 40 */ 1578, 1578, 1254, 1254, 1254, 1254, 1254, 1563, 1562, 1254, + /* 50 */ 1254, 1254, 1406, 1254, 1413, 1254, 1254, 1254, 1254, 1254, + /* 60 */ 1492, 1493, 1254, 1254, 1254, 1543, 1545, 1508, 1420, 1419, + /* 70 */ 1418, 1417, 1526, 1385, 1411, 1404, 1408, 1487, 1488, 1486, + /* 80 */ 1641, 1493, 1492, 1254, 1407, 1455, 1471, 1454, 1254, 1254, + /* 90 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, + /* 100 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, + /* 110 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, + /* 120 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, + /* 130 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, + /* 140 */ 1463, 1470, 1469, 1468, 1477, 1467, 1464, 1457, 1456, 1458, + /* 150 */ 1459, 1278, 1254, 1275, 1329, 1254, 1254, 1254, 1254, 1254, + /* 160 */ 1460, 1287, 1448, 1447, 1446, 1254, 1474, 1461, 1473, 1472, + /* 170 */ 1551, 1615, 1614, 1509, 1254, 1254, 1254, 1254, 1254, 1254, + /* 180 */ 1578, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, + /* 190 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, + /* 200 */ 1254, 1254, 1254, 1387, 1578, 1578, 1254, 1287, 1578, 1578, + /* 210 */ 1388, 1388, 1283, 1283, 1391, 1558, 1358, 1358, 1358, 1358, + /* 220 */ 1367, 1358, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, + /* 230 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1548, 1546, 1254, + /* 240 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, + /* 250 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, + /* 260 */ 1254, 1254, 1254, 1254, 1254, 1254, 1363, 1254, 1254, 1254, + /* 270 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1608, 1254, + /* 280 */ 1521, 1343, 1363, 1363, 1363, 1363, 1365, 1344, 1342, 1357, + /* 290 */ 1288, 1261, 1655, 1423, 1412, 1364, 1412, 1652, 1410, 1423, + /* 300 */ 1423, 1410, 1423, 1364, 1652, 1304, 1630, 1299, 1397, 1397, + /* 310 */ 1397, 1387, 1387, 1387, 1387, 1391, 1391, 1489, 1364, 1357, + /* 320 */ 1254, 1655, 1655, 1373, 1373, 1654, 1654, 1373, 1509, 1638, + /* 330 */ 1432, 1332, 1338, 1338, 1338, 1338, 1373, 1272, 1410, 1638, + /* 340 */ 1638, 1410, 1432, 1332, 1410, 1332, 1410, 1373, 1272, 1525, + /* 350 */ 1649, 1373, 1272, 1499, 1373, 1272, 1373, 1272, 1499, 1330, + /* 360 */ 1330, 1330, 1319, 1254, 1254, 1499, 1330, 1304, 1330, 1319, + /* 370 */ 1330, 1330, 1596, 1254, 1503, 1503, 1499, 1373, 1588, 1588, + /* 380 */ 1400, 1400, 1405, 1391, 1494, 1373, 1254, 1405, 1403, 1401, + /* 390 */ 1410, 1322, 1611, 1611, 1607, 1607, 1607, 1660, 1660, 1558, + /* 400 */ 1623, 1287, 1287, 1287, 1287, 1623, 1306, 1306, 1288, 1288, + /* 410 */ 1287, 1623, 1254, 1254, 1254, 1254, 1254, 1254, 1618, 1254, + /* 420 */ 1553, 1510, 1377, 1254, 1254, 1254, 1254, 1254, 1254, 1254, + /* 430 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1564, + /* 440 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, + /* 450 */ 1254, 1437, 1254, 1257, 1555, 1254, 1254, 1254, 1254, 1254, + /* 460 */ 1254, 1254, 1254, 1414, 1415, 1378, 1254, 1254, 1254, 1254, + /* 470 */ 1254, 1254, 1254, 1429, 1254, 1254, 1254, 1424, 1254, 1254, + /* 480 */ 1254, 1254, 1254, 1254, 1254, 1254, 1651, 1254, 1254, 1254, + /* 490 */ 1254, 1254, 1254, 1524, 1523, 1254, 1254, 1375, 1254, 1254, + /* 500 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, + /* 510 */ 1254, 1302, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, + /* 520 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, + /* 530 */ 1254, 1254, 1254, 1254, 1254, 1402, 1254, 1254, 1254, 1254, + /* 540 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, + /* 550 */ 1593, 1392, 1254, 1254, 1254, 1254, 1642, 1254, 1254, 1254, + /* 560 */ 1254, 1352, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, + /* 570 */ 1254, 1254, 1254, 1634, 1346, 1438, 1254, 1441, 1276, 1254, + /* 580 */ 1266, 1254, 1254, }; /********** End of lemon-generated parsing tables *****************************/ @@ -172521,8 +173902,8 @@ static const YYCODETYPE yyFallback[] = { 0, /* TRUEFALSE => nothing */ 0, /* ISNOT => nothing */ 0, /* FUNCTION => nothing */ - 0, /* UMINUS => nothing */ 0, /* UPLUS => nothing */ + 0, /* UMINUS => nothing */ 0, /* TRUTH => nothing */ 0, /* REGISTER => nothing */ 0, /* VECTOR => nothing */ @@ -172531,6 +173912,7 @@ static const YYCODETYPE yyFallback[] = { 0, /* ASTERISK => nothing */ 0, /* SPAN => nothing */ 0, /* ERROR => nothing */ + 0, /* QNUMBER => nothing */ 0, /* SPACE => nothing */ 0, /* ILLEGAL => nothing */ }; @@ -172573,14 +173955,9 @@ struct yyParser { #endif sqlite3ParserARG_SDECL /* A place to hold %extra_argument */ sqlite3ParserCTX_SDECL /* A place to hold %extra_context */ -#if YYSTACKDEPTH<=0 - int yystksz; /* Current side of the stack */ - yyStackEntry *yystack; /* The parser's stack */ - yyStackEntry yystk0; /* First stack entry */ -#else - yyStackEntry yystack[YYSTACKDEPTH]; /* The parser's stack */ - yyStackEntry *yystackEnd; /* Last entry in the stack */ -#endif + yyStackEntry *yystackEnd; /* Last entry in the stack */ + yyStackEntry *yystack; /* The parser stack */ + yyStackEntry yystk0[YYSTACKDEPTH]; /* Initial stack space */ }; typedef struct yyParser yyParser; @@ -172794,8 +174171,8 @@ static const char *const yyTokenName[] = { /* 170 */ "TRUEFALSE", /* 171 */ "ISNOT", /* 172 */ "FUNCTION", - /* 173 */ "UMINUS", - /* 174 */ "UPLUS", + /* 173 */ "UPLUS", + /* 174 */ "UMINUS", /* 175 */ "TRUTH", /* 176 */ "REGISTER", /* 177 */ "VECTOR", @@ -172804,142 +174181,145 @@ static const char *const yyTokenName[] = { /* 180 */ "ASTERISK", /* 181 */ "SPAN", /* 182 */ "ERROR", - /* 183 */ "SPACE", - /* 184 */ "ILLEGAL", - /* 185 */ "input", - /* 186 */ "cmdlist", - /* 187 */ "ecmd", - /* 188 */ "cmdx", - /* 189 */ "explain", - /* 190 */ "cmd", - /* 191 */ "transtype", - /* 192 */ "trans_opt", - /* 193 */ "nm", - /* 194 */ "savepoint_opt", - /* 195 */ "create_table", - /* 196 */ "create_table_args", - /* 197 */ "createkw", - /* 198 */ "temp", - /* 199 */ "ifnotexists", - /* 200 */ "dbnm", - /* 201 */ "columnlist", - /* 202 */ "conslist_opt", - /* 203 */ "table_option_set", - /* 204 */ "select", - /* 205 */ "table_option", - /* 206 */ "columnname", - /* 207 */ "carglist", - /* 208 */ "typetoken", - /* 209 */ "typename", - /* 210 */ "signed", - /* 211 */ "plus_num", - /* 212 */ "minus_num", - /* 213 */ "scanpt", - /* 214 */ "scantok", - /* 215 */ "ccons", - /* 216 */ "term", - /* 217 */ "expr", - /* 218 */ "onconf", - /* 219 */ "sortorder", - /* 220 */ "autoinc", - /* 221 */ "eidlist_opt", - /* 222 */ "refargs", - /* 223 */ "defer_subclause", - /* 224 */ "generated", - /* 225 */ "refarg", - /* 226 */ "refact", - /* 227 */ "init_deferred_pred_opt", - /* 228 */ "conslist", - /* 229 */ "tconscomma", - /* 230 */ "tcons", - /* 231 */ "sortlist", - /* 232 */ "eidlist", - /* 233 */ "defer_subclause_opt", - /* 234 */ "orconf", - /* 235 */ "resolvetype", - /* 236 */ "raisetype", - /* 237 */ "ifexists", - /* 238 */ "fullname", - /* 239 */ "selectnowith", - /* 240 */ "oneselect", - /* 241 */ "wqlist", - /* 242 */ "multiselect_op", - /* 243 */ "distinct", - /* 244 */ "selcollist", - /* 245 */ "from", - /* 246 */ "where_opt", - /* 247 */ "groupby_opt", - /* 248 */ "having_opt", - /* 249 */ "orderby_opt", - /* 250 */ "limit_opt", - /* 251 */ "window_clause", - /* 252 */ "values", - /* 253 */ "nexprlist", - /* 254 */ "sclp", - /* 255 */ "as", - /* 256 */ "seltablist", - /* 257 */ "stl_prefix", - /* 258 */ "joinop", - /* 259 */ "on_using", - /* 260 */ "indexed_by", - /* 261 */ "exprlist", - /* 262 */ "xfullname", - /* 263 */ "idlist", - /* 264 */ "indexed_opt", - /* 265 */ "nulls", - /* 266 */ "with", - /* 267 */ "where_opt_ret", - /* 268 */ "setlist", - /* 269 */ "insert_cmd", - /* 270 */ "idlist_opt", - /* 271 */ "upsert", - /* 272 */ "returning", - /* 273 */ "filter_over", - /* 274 */ "likeop", - /* 275 */ "between_op", - /* 276 */ "in_op", - /* 277 */ "paren_exprlist", - /* 278 */ "case_operand", - /* 279 */ "case_exprlist", - /* 280 */ "case_else", - /* 281 */ "uniqueflag", - /* 282 */ "collate", - /* 283 */ "vinto", - /* 284 */ "nmnum", - /* 285 */ "trigger_decl", - /* 286 */ "trigger_cmd_list", - /* 287 */ "trigger_time", - /* 288 */ "trigger_event", - /* 289 */ "foreach_clause", - /* 290 */ "when_clause", - /* 291 */ "trigger_cmd", - /* 292 */ "trnm", - /* 293 */ "tridxby", - /* 294 */ "database_kw_opt", - /* 295 */ "key_opt", - /* 296 */ "add_column_fullname", - /* 297 */ "kwcolumn_opt", - /* 298 */ "create_vtab", - /* 299 */ "vtabarglist", - /* 300 */ "vtabarg", - /* 301 */ "vtabargtoken", - /* 302 */ "lp", - /* 303 */ "anylist", - /* 304 */ "wqitem", - /* 305 */ "wqas", - /* 306 */ "windowdefn_list", - /* 307 */ "windowdefn", - /* 308 */ "window", - /* 309 */ "frame_opt", - /* 310 */ "part_opt", - /* 311 */ "filter_clause", - /* 312 */ "over_clause", - /* 313 */ "range_or_rows", - /* 314 */ "frame_bound", - /* 315 */ "frame_bound_s", - /* 316 */ "frame_bound_e", - /* 317 */ "frame_exclude_opt", - /* 318 */ "frame_exclude", + /* 183 */ "QNUMBER", + /* 184 */ "SPACE", + /* 185 */ "ILLEGAL", + /* 186 */ "input", + /* 187 */ "cmdlist", + /* 188 */ "ecmd", + /* 189 */ "cmdx", + /* 190 */ "explain", + /* 191 */ "cmd", + /* 192 */ "transtype", + /* 193 */ "trans_opt", + /* 194 */ "nm", + /* 195 */ "savepoint_opt", + /* 196 */ "create_table", + /* 197 */ "create_table_args", + /* 198 */ "createkw", + /* 199 */ "temp", + /* 200 */ "ifnotexists", + /* 201 */ "dbnm", + /* 202 */ "columnlist", + /* 203 */ "conslist_opt", + /* 204 */ "table_option_set", + /* 205 */ "select", + /* 206 */ "table_option", + /* 207 */ "columnname", + /* 208 */ "carglist", + /* 209 */ "typetoken", + /* 210 */ "typename", + /* 211 */ "signed", + /* 212 */ "plus_num", + /* 213 */ "minus_num", + /* 214 */ "scanpt", + /* 215 */ "scantok", + /* 216 */ "ccons", + /* 217 */ "term", + /* 218 */ "expr", + /* 219 */ "onconf", + /* 220 */ "sortorder", + /* 221 */ "autoinc", + /* 222 */ "eidlist_opt", + /* 223 */ "refargs", + /* 224 */ "defer_subclause", + /* 225 */ "generated", + /* 226 */ "refarg", + /* 227 */ "refact", + /* 228 */ "init_deferred_pred_opt", + /* 229 */ "conslist", + /* 230 */ "tconscomma", + /* 231 */ "tcons", + /* 232 */ "sortlist", + /* 233 */ "eidlist", + /* 234 */ "defer_subclause_opt", + /* 235 */ "orconf", + /* 236 */ "resolvetype", + /* 237 */ "raisetype", + /* 238 */ "ifexists", + /* 239 */ "fullname", + /* 240 */ "selectnowith", + /* 241 */ "oneselect", + /* 242 */ "wqlist", + /* 243 */ "multiselect_op", + /* 244 */ "distinct", + /* 245 */ "selcollist", + /* 246 */ "from", + /* 247 */ "where_opt", + /* 248 */ "groupby_opt", + /* 249 */ "having_opt", + /* 250 */ "orderby_opt", + /* 251 */ "limit_opt", + /* 252 */ "window_clause", + /* 253 */ "values", + /* 254 */ "nexprlist", + /* 255 */ "mvalues", + /* 256 */ "sclp", + /* 257 */ "as", + /* 258 */ "seltablist", + /* 259 */ "stl_prefix", + /* 260 */ "joinop", + /* 261 */ "on_using", + /* 262 */ "indexed_by", + /* 263 */ "exprlist", + /* 264 */ "xfullname", + /* 265 */ "idlist", + /* 266 */ "indexed_opt", + /* 267 */ "nulls", + /* 268 */ "with", + /* 269 */ "where_opt_ret", + /* 270 */ "setlist", + /* 271 */ "insert_cmd", + /* 272 */ "idlist_opt", + /* 273 */ "upsert", + /* 274 */ "returning", + /* 275 */ "filter_over", + /* 276 */ "likeop", + /* 277 */ "between_op", + /* 278 */ "in_op", + /* 279 */ "paren_exprlist", + /* 280 */ "case_operand", + /* 281 */ "case_exprlist", + /* 282 */ "case_else", + /* 283 */ "uniqueflag", + /* 284 */ "collate", + /* 285 */ "vinto", + /* 286 */ "nmnum", + /* 287 */ "trigger_decl", + /* 288 */ "trigger_cmd_list", + /* 289 */ "trigger_time", + /* 290 */ "trigger_event", + /* 291 */ "foreach_clause", + /* 292 */ "when_clause", + /* 293 */ "trigger_cmd", + /* 294 */ "trnm", + /* 295 */ "tridxby", + /* 296 */ "database_kw_opt", + /* 297 */ "key_opt", + /* 298 */ "add_column_fullname", + /* 299 */ "kwcolumn_opt", + /* 300 */ "create_vtab", + /* 301 */ "vtabarglist", + /* 302 */ "vtabarg", + /* 303 */ "vtabargtoken", + /* 304 */ "lp", + /* 305 */ "anylist", + /* 306 */ "wqitem", + /* 307 */ "wqas", + /* 308 */ "withnm", + /* 309 */ "windowdefn_list", + /* 310 */ "windowdefn", + /* 311 */ "window", + /* 312 */ "frame_opt", + /* 313 */ "part_opt", + /* 314 */ "filter_clause", + /* 315 */ "over_clause", + /* 316 */ "range_or_rows", + /* 317 */ "frame_bound", + /* 318 */ "frame_bound_s", + /* 319 */ "frame_bound_e", + /* 320 */ "frame_exclude_opt", + /* 321 */ "frame_exclude", }; #endif /* defined(YYCOVERAGE) || !defined(NDEBUG) */ @@ -173042,351 +174422,363 @@ static const char *const yyRuleName[] = { /* 92 */ "oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt", /* 93 */ "oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt", /* 94 */ "values ::= VALUES LP nexprlist RP", - /* 95 */ "values ::= values COMMA LP nexprlist RP", - /* 96 */ "distinct ::= DISTINCT", - /* 97 */ "distinct ::= ALL", - /* 98 */ "distinct ::=", - /* 99 */ "sclp ::=", - /* 100 */ "selcollist ::= sclp scanpt expr scanpt as", - /* 101 */ "selcollist ::= sclp scanpt STAR", - /* 102 */ "selcollist ::= sclp scanpt nm DOT STAR", - /* 103 */ "as ::= AS nm", - /* 104 */ "as ::=", - /* 105 */ "from ::=", - /* 106 */ "from ::= FROM seltablist", - /* 107 */ "stl_prefix ::= seltablist joinop", - /* 108 */ "stl_prefix ::=", - /* 109 */ "seltablist ::= stl_prefix nm dbnm as on_using", - /* 110 */ "seltablist ::= stl_prefix nm dbnm as indexed_by on_using", - /* 111 */ "seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_using", - /* 112 */ "seltablist ::= stl_prefix LP select RP as on_using", - /* 113 */ "seltablist ::= stl_prefix LP seltablist RP as on_using", - /* 114 */ "dbnm ::=", - /* 115 */ "dbnm ::= DOT nm", - /* 116 */ "fullname ::= nm", - /* 117 */ "fullname ::= nm DOT nm", - /* 118 */ "xfullname ::= nm", - /* 119 */ "xfullname ::= nm DOT nm", - /* 120 */ "xfullname ::= nm DOT nm AS nm", - /* 121 */ "xfullname ::= nm AS nm", - /* 122 */ "joinop ::= COMMA|JOIN", - /* 123 */ "joinop ::= JOIN_KW JOIN", - /* 124 */ "joinop ::= JOIN_KW nm JOIN", - /* 125 */ "joinop ::= JOIN_KW nm nm JOIN", - /* 126 */ "on_using ::= ON expr", - /* 127 */ "on_using ::= USING LP idlist RP", - /* 128 */ "on_using ::=", - /* 129 */ "indexed_opt ::=", - /* 130 */ "indexed_by ::= INDEXED BY nm", - /* 131 */ "indexed_by ::= NOT INDEXED", - /* 132 */ "orderby_opt ::=", - /* 133 */ "orderby_opt ::= ORDER BY sortlist", - /* 134 */ "sortlist ::= sortlist COMMA expr sortorder nulls", - /* 135 */ "sortlist ::= expr sortorder nulls", - /* 136 */ "sortorder ::= ASC", - /* 137 */ "sortorder ::= DESC", - /* 138 */ "sortorder ::=", - /* 139 */ "nulls ::= NULLS FIRST", - /* 140 */ "nulls ::= NULLS LAST", - /* 141 */ "nulls ::=", - /* 142 */ "groupby_opt ::=", - /* 143 */ "groupby_opt ::= GROUP BY nexprlist", - /* 144 */ "having_opt ::=", - /* 145 */ "having_opt ::= HAVING expr", - /* 146 */ "limit_opt ::=", - /* 147 */ "limit_opt ::= LIMIT expr", - /* 148 */ "limit_opt ::= LIMIT expr OFFSET expr", - /* 149 */ "limit_opt ::= LIMIT expr COMMA expr", - /* 150 */ "cmd ::= with DELETE FROM xfullname indexed_opt where_opt_ret", - /* 151 */ "where_opt ::=", - /* 152 */ "where_opt ::= WHERE expr", - /* 153 */ "where_opt_ret ::=", - /* 154 */ "where_opt_ret ::= WHERE expr", - /* 155 */ "where_opt_ret ::= RETURNING selcollist", - /* 156 */ "where_opt_ret ::= WHERE expr RETURNING selcollist", - /* 157 */ "cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt_ret", - /* 158 */ "setlist ::= setlist COMMA nm EQ expr", - /* 159 */ "setlist ::= setlist COMMA LP idlist RP EQ expr", - /* 160 */ "setlist ::= nm EQ expr", - /* 161 */ "setlist ::= LP idlist RP EQ expr", - /* 162 */ "cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert", - /* 163 */ "cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES returning", - /* 164 */ "upsert ::=", - /* 165 */ "upsert ::= RETURNING selcollist", - /* 166 */ "upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert", - /* 167 */ "upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert", - /* 168 */ "upsert ::= ON CONFLICT DO NOTHING returning", - /* 169 */ "upsert ::= ON CONFLICT DO UPDATE SET setlist where_opt returning", - /* 170 */ "returning ::= RETURNING selcollist", - /* 171 */ "insert_cmd ::= INSERT orconf", - /* 172 */ "insert_cmd ::= REPLACE", - /* 173 */ "idlist_opt ::=", - /* 174 */ "idlist_opt ::= LP idlist RP", - /* 175 */ "idlist ::= idlist COMMA nm", - /* 176 */ "idlist ::= nm", - /* 177 */ "expr ::= LP expr RP", - /* 178 */ "expr ::= ID|INDEXED|JOIN_KW", - /* 179 */ "expr ::= nm DOT nm", - /* 180 */ "expr ::= nm DOT nm DOT nm", - /* 181 */ "term ::= NULL|FLOAT|BLOB", - /* 182 */ "term ::= STRING", - /* 183 */ "term ::= INTEGER", - /* 184 */ "expr ::= VARIABLE", - /* 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 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", + /* 95 */ "oneselect ::= mvalues", + /* 96 */ "mvalues ::= values COMMA LP nexprlist RP", + /* 97 */ "mvalues ::= mvalues COMMA LP nexprlist RP", + /* 98 */ "distinct ::= DISTINCT", + /* 99 */ "distinct ::= ALL", + /* 100 */ "distinct ::=", + /* 101 */ "sclp ::=", + /* 102 */ "selcollist ::= sclp scanpt expr scanpt as", + /* 103 */ "selcollist ::= sclp scanpt STAR", + /* 104 */ "selcollist ::= sclp scanpt nm DOT STAR", + /* 105 */ "as ::= AS nm", + /* 106 */ "as ::=", + /* 107 */ "from ::=", + /* 108 */ "from ::= FROM seltablist", + /* 109 */ "stl_prefix ::= seltablist joinop", + /* 110 */ "stl_prefix ::=", + /* 111 */ "seltablist ::= stl_prefix nm dbnm as on_using", + /* 112 */ "seltablist ::= stl_prefix nm dbnm as indexed_by on_using", + /* 113 */ "seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_using", + /* 114 */ "seltablist ::= stl_prefix LP select RP as on_using", + /* 115 */ "seltablist ::= stl_prefix LP seltablist RP as on_using", + /* 116 */ "dbnm ::=", + /* 117 */ "dbnm ::= DOT nm", + /* 118 */ "fullname ::= nm", + /* 119 */ "fullname ::= nm DOT nm", + /* 120 */ "xfullname ::= nm", + /* 121 */ "xfullname ::= nm DOT nm", + /* 122 */ "xfullname ::= nm DOT nm AS nm", + /* 123 */ "xfullname ::= nm AS nm", + /* 124 */ "joinop ::= COMMA|JOIN", + /* 125 */ "joinop ::= JOIN_KW JOIN", + /* 126 */ "joinop ::= JOIN_KW nm JOIN", + /* 127 */ "joinop ::= JOIN_KW nm nm JOIN", + /* 128 */ "on_using ::= ON expr", + /* 129 */ "on_using ::= USING LP idlist RP", + /* 130 */ "on_using ::=", + /* 131 */ "indexed_opt ::=", + /* 132 */ "indexed_by ::= INDEXED BY nm", + /* 133 */ "indexed_by ::= NOT INDEXED", + /* 134 */ "orderby_opt ::=", + /* 135 */ "orderby_opt ::= ORDER BY sortlist", + /* 136 */ "sortlist ::= sortlist COMMA expr sortorder nulls", + /* 137 */ "sortlist ::= expr sortorder nulls", + /* 138 */ "sortorder ::= ASC", + /* 139 */ "sortorder ::= DESC", + /* 140 */ "sortorder ::=", + /* 141 */ "nulls ::= NULLS FIRST", + /* 142 */ "nulls ::= NULLS LAST", + /* 143 */ "nulls ::=", + /* 144 */ "groupby_opt ::=", + /* 145 */ "groupby_opt ::= GROUP BY nexprlist", + /* 146 */ "having_opt ::=", + /* 147 */ "having_opt ::= HAVING expr", + /* 148 */ "limit_opt ::=", + /* 149 */ "limit_opt ::= LIMIT expr", + /* 150 */ "limit_opt ::= LIMIT expr OFFSET expr", + /* 151 */ "limit_opt ::= LIMIT expr COMMA expr", + /* 152 */ "cmd ::= with DELETE FROM xfullname indexed_opt where_opt_ret", + /* 153 */ "where_opt ::=", + /* 154 */ "where_opt ::= WHERE expr", + /* 155 */ "where_opt_ret ::=", + /* 156 */ "where_opt_ret ::= WHERE expr", + /* 157 */ "where_opt_ret ::= RETURNING selcollist", + /* 158 */ "where_opt_ret ::= WHERE expr RETURNING selcollist", + /* 159 */ "cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt_ret", + /* 160 */ "setlist ::= setlist COMMA nm EQ expr", + /* 161 */ "setlist ::= setlist COMMA LP idlist RP EQ expr", + /* 162 */ "setlist ::= nm EQ expr", + /* 163 */ "setlist ::= LP idlist RP EQ expr", + /* 164 */ "cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert", + /* 165 */ "cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES returning", + /* 166 */ "upsert ::=", + /* 167 */ "upsert ::= RETURNING selcollist", + /* 168 */ "upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert", + /* 169 */ "upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert", + /* 170 */ "upsert ::= ON CONFLICT DO NOTHING returning", + /* 171 */ "upsert ::= ON CONFLICT DO UPDATE SET setlist where_opt returning", + /* 172 */ "returning ::= RETURNING selcollist", + /* 173 */ "insert_cmd ::= INSERT orconf", + /* 174 */ "insert_cmd ::= REPLACE", + /* 175 */ "idlist_opt ::=", + /* 176 */ "idlist_opt ::= LP idlist RP", + /* 177 */ "idlist ::= idlist COMMA nm", + /* 178 */ "idlist ::= nm", + /* 179 */ "expr ::= LP expr RP", + /* 180 */ "expr ::= ID|INDEXED|JOIN_KW", + /* 181 */ "expr ::= nm DOT nm", + /* 182 */ "expr ::= nm DOT nm DOT nm", + /* 183 */ "term ::= NULL|FLOAT|BLOB", + /* 184 */ "term ::= STRING", + /* 185 */ "term ::= INTEGER", + /* 186 */ "expr ::= VARIABLE", + /* 187 */ "expr ::= expr COLLATE ID|STRING", + /* 188 */ "expr ::= CAST LP expr AS typetoken RP", + /* 189 */ "expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP", + /* 190 */ "expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP", + /* 191 */ "expr ::= ID|INDEXED|JOIN_KW LP STAR RP", + /* 192 */ "expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP filter_over", + /* 193 */ "expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP filter_over", + /* 194 */ "expr ::= ID|INDEXED|JOIN_KW LP STAR RP filter_over", + /* 195 */ "term ::= CTIME_KW", + /* 196 */ "expr ::= LP nexprlist COMMA expr RP", + /* 197 */ "expr ::= expr AND expr", + /* 198 */ "expr ::= expr OR expr", + /* 199 */ "expr ::= expr LT|GT|GE|LE expr", + /* 200 */ "expr ::= expr EQ|NE expr", + /* 201 */ "expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr", + /* 202 */ "expr ::= expr PLUS|MINUS expr", + /* 203 */ "expr ::= expr STAR|SLASH|REM expr", + /* 204 */ "expr ::= expr CONCAT expr", + /* 205 */ "likeop ::= NOT LIKE_KW|MATCH", + /* 206 */ "expr ::= expr likeop expr", + /* 207 */ "expr ::= expr likeop expr ESCAPE expr", + /* 208 */ "expr ::= expr ISNULL|NOTNULL", + /* 209 */ "expr ::= expr NOT NULL", + /* 210 */ "expr ::= expr IS expr", + /* 211 */ "expr ::= expr IS NOT expr", + /* 212 */ "expr ::= expr IS NOT DISTINCT FROM expr", + /* 213 */ "expr ::= expr IS DISTINCT FROM expr", + /* 214 */ "expr ::= NOT expr", + /* 215 */ "expr ::= BITNOT expr", + /* 216 */ "expr ::= PLUS|MINUS expr", + /* 217 */ "expr ::= expr PTR expr", + /* 218 */ "between_op ::= BETWEEN", + /* 219 */ "between_op ::= NOT BETWEEN", + /* 220 */ "expr ::= expr between_op expr AND expr", + /* 221 */ "in_op ::= IN", + /* 222 */ "in_op ::= NOT IN", + /* 223 */ "expr ::= expr in_op LP exprlist RP", + /* 224 */ "expr ::= LP select RP", + /* 225 */ "expr ::= expr in_op LP select RP", + /* 226 */ "expr ::= expr in_op nm dbnm paren_exprlist", + /* 227 */ "expr ::= EXISTS LP select RP", + /* 228 */ "expr ::= CASE case_operand case_exprlist case_else END", + /* 229 */ "case_exprlist ::= case_exprlist WHEN expr THEN expr", + /* 230 */ "case_exprlist ::= WHEN expr THEN expr", + /* 231 */ "case_else ::= ELSE expr", + /* 232 */ "case_else ::=", + /* 233 */ "case_operand ::=", + /* 234 */ "exprlist ::=", + /* 235 */ "nexprlist ::= nexprlist COMMA expr", + /* 236 */ "nexprlist ::= expr", + /* 237 */ "paren_exprlist ::=", + /* 238 */ "paren_exprlist ::= LP exprlist RP", + /* 239 */ "cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt", + /* 240 */ "uniqueflag ::= UNIQUE", + /* 241 */ "uniqueflag ::=", + /* 242 */ "eidlist_opt ::=", + /* 243 */ "eidlist_opt ::= LP eidlist RP", + /* 244 */ "eidlist ::= eidlist COMMA nm collate sortorder", + /* 245 */ "eidlist ::= nm collate sortorder", + /* 246 */ "collate ::=", + /* 247 */ "collate ::= COLLATE ID|STRING", + /* 248 */ "cmd ::= DROP INDEX ifexists fullname", + /* 249 */ "cmd ::= VACUUM vinto", + /* 250 */ "cmd ::= VACUUM nm vinto", + /* 251 */ "vinto ::= INTO expr", + /* 252 */ "vinto ::=", + /* 253 */ "cmd ::= PRAGMA nm dbnm", + /* 254 */ "cmd ::= PRAGMA nm dbnm EQ nmnum", + /* 255 */ "cmd ::= PRAGMA nm dbnm LP nmnum RP", + /* 256 */ "cmd ::= PRAGMA nm dbnm EQ minus_num", + /* 257 */ "cmd ::= PRAGMA nm dbnm LP minus_num RP", + /* 258 */ "plus_num ::= PLUS INTEGER|FLOAT", + /* 259 */ "minus_num ::= MINUS INTEGER|FLOAT", + /* 260 */ "cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END", + /* 261 */ "trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause", + /* 262 */ "trigger_time ::= BEFORE|AFTER", + /* 263 */ "trigger_time ::= INSTEAD OF", + /* 264 */ "trigger_time ::=", + /* 265 */ "trigger_event ::= DELETE|INSERT", + /* 266 */ "trigger_event ::= UPDATE", + /* 267 */ "trigger_event ::= UPDATE OF idlist", + /* 268 */ "when_clause ::=", + /* 269 */ "when_clause ::= WHEN expr", + /* 270 */ "trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI", + /* 271 */ "trigger_cmd_list ::= trigger_cmd SEMI", + /* 272 */ "trnm ::= nm DOT nm", + /* 273 */ "tridxby ::= INDEXED BY nm", + /* 274 */ "tridxby ::= NOT INDEXED", + /* 275 */ "trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt", + /* 276 */ "trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt", + /* 277 */ "trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt", + /* 278 */ "trigger_cmd ::= scanpt select scanpt", + /* 279 */ "expr ::= RAISE LP IGNORE RP", + /* 280 */ "expr ::= RAISE LP raisetype COMMA nm RP", + /* 281 */ "raisetype ::= ROLLBACK", + /* 282 */ "raisetype ::= ABORT", + /* 283 */ "raisetype ::= FAIL", + /* 284 */ "cmd ::= DROP TRIGGER ifexists fullname", + /* 285 */ "cmd ::= ATTACH database_kw_opt expr AS expr key_opt", + /* 286 */ "cmd ::= DETACH database_kw_opt expr", + /* 287 */ "key_opt ::=", + /* 288 */ "key_opt ::= KEY expr", + /* 289 */ "cmd ::= REINDEX", + /* 290 */ "cmd ::= REINDEX nm dbnm", + /* 291 */ "cmd ::= ANALYZE", + /* 292 */ "cmd ::= ANALYZE nm dbnm", + /* 293 */ "cmd ::= ALTER TABLE fullname RENAME TO nm", + /* 294 */ "cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist", + /* 295 */ "cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm", + /* 296 */ "add_column_fullname ::= fullname", + /* 297 */ "cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm", + /* 298 */ "cmd ::= create_vtab", + /* 299 */ "cmd ::= create_vtab LP vtabarglist RP", + /* 300 */ "create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm", + /* 301 */ "vtabarg ::=", + /* 302 */ "vtabargtoken ::= ANY", + /* 303 */ "vtabargtoken ::= lp anylist RP", + /* 304 */ "lp ::= LP", + /* 305 */ "with ::= WITH wqlist", + /* 306 */ "with ::= WITH RECURSIVE wqlist", + /* 307 */ "wqas ::= AS", + /* 308 */ "wqas ::= AS MATERIALIZED", + /* 309 */ "wqas ::= AS NOT MATERIALIZED", + /* 310 */ "wqitem ::= withnm eidlist_opt wqas LP select RP", + /* 311 */ "withnm ::= nm", + /* 312 */ "wqlist ::= wqitem", + /* 313 */ "wqlist ::= wqlist COMMA wqitem", + /* 314 */ "windowdefn_list ::= windowdefn_list COMMA windowdefn", + /* 315 */ "windowdefn ::= nm AS LP window RP", + /* 316 */ "window ::= PARTITION BY nexprlist orderby_opt frame_opt", + /* 317 */ "window ::= nm PARTITION BY nexprlist orderby_opt frame_opt", + /* 318 */ "window ::= ORDER BY sortlist frame_opt", + /* 319 */ "window ::= nm ORDER BY sortlist frame_opt", + /* 320 */ "window ::= nm frame_opt", + /* 321 */ "frame_opt ::=", + /* 322 */ "frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt", + /* 323 */ "frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt", + /* 324 */ "range_or_rows ::= RANGE|ROWS|GROUPS", + /* 325 */ "frame_bound_s ::= frame_bound", + /* 326 */ "frame_bound_s ::= UNBOUNDED PRECEDING", + /* 327 */ "frame_bound_e ::= frame_bound", + /* 328 */ "frame_bound_e ::= UNBOUNDED FOLLOWING", + /* 329 */ "frame_bound ::= expr PRECEDING|FOLLOWING", + /* 330 */ "frame_bound ::= CURRENT ROW", + /* 331 */ "frame_exclude_opt ::=", + /* 332 */ "frame_exclude_opt ::= EXCLUDE frame_exclude", + /* 333 */ "frame_exclude ::= NO OTHERS", + /* 334 */ "frame_exclude ::= CURRENT ROW", + /* 335 */ "frame_exclude ::= GROUP|TIES", + /* 336 */ "window_clause ::= WINDOW windowdefn_list", + /* 337 */ "filter_over ::= filter_clause over_clause", + /* 338 */ "filter_over ::= over_clause", + /* 339 */ "filter_over ::= filter_clause", + /* 340 */ "over_clause ::= OVER LP window RP", + /* 341 */ "over_clause ::= OVER nm", + /* 342 */ "filter_clause ::= FILTER LP WHERE expr RP", + /* 343 */ "term ::= QNUMBER", + /* 344 */ "input ::= cmdlist", + /* 345 */ "cmdlist ::= cmdlist ecmd", + /* 346 */ "cmdlist ::= ecmd", + /* 347 */ "ecmd ::= SEMI", + /* 348 */ "ecmd ::= cmdx SEMI", + /* 349 */ "ecmd ::= explain cmdx SEMI", + /* 350 */ "trans_opt ::=", + /* 351 */ "trans_opt ::= TRANSACTION", + /* 352 */ "trans_opt ::= TRANSACTION nm", + /* 353 */ "savepoint_opt ::= SAVEPOINT", + /* 354 */ "savepoint_opt ::=", + /* 355 */ "cmd ::= create_table create_table_args", + /* 356 */ "table_option_set ::= table_option", + /* 357 */ "columnlist ::= columnlist COMMA columnname carglist", + /* 358 */ "columnlist ::= columnname carglist", + /* 359 */ "nm ::= ID|INDEXED|JOIN_KW", + /* 360 */ "nm ::= STRING", + /* 361 */ "typetoken ::= typename", + /* 362 */ "typename ::= ID|STRING", + /* 363 */ "signed ::= plus_num", + /* 364 */ "signed ::= minus_num", + /* 365 */ "carglist ::= carglist ccons", + /* 366 */ "carglist ::=", + /* 367 */ "ccons ::= NULL onconf", + /* 368 */ "ccons ::= GENERATED ALWAYS AS generated", + /* 369 */ "ccons ::= AS generated", + /* 370 */ "conslist_opt ::= COMMA conslist", + /* 371 */ "conslist ::= conslist tconscomma tcons", + /* 372 */ "conslist ::= tcons", + /* 373 */ "tconscomma ::=", + /* 374 */ "defer_subclause_opt ::= defer_subclause", + /* 375 */ "resolvetype ::= raisetype", + /* 376 */ "selectnowith ::= oneselect", + /* 377 */ "oneselect ::= values", + /* 378 */ "sclp ::= selcollist COMMA", + /* 379 */ "as ::= ID|STRING", + /* 380 */ "indexed_opt ::= indexed_by", + /* 381 */ "returning ::=", + /* 382 */ "expr ::= term", + /* 383 */ "likeop ::= LIKE_KW|MATCH", + /* 384 */ "case_operand ::= expr", + /* 385 */ "exprlist ::= nexprlist", + /* 386 */ "nmnum ::= plus_num", + /* 387 */ "nmnum ::= nm", + /* 388 */ "nmnum ::= ON", + /* 389 */ "nmnum ::= DELETE", + /* 390 */ "nmnum ::= DEFAULT", + /* 391 */ "plus_num ::= INTEGER|FLOAT", + /* 392 */ "foreach_clause ::=", + /* 393 */ "foreach_clause ::= FOR EACH ROW", + /* 394 */ "trnm ::= nm", + /* 395 */ "tridxby ::=", + /* 396 */ "database_kw_opt ::= DATABASE", + /* 397 */ "database_kw_opt ::=", + /* 398 */ "kwcolumn_opt ::=", + /* 399 */ "kwcolumn_opt ::= COLUMNKW", + /* 400 */ "vtabarglist ::= vtabarg", + /* 401 */ "vtabarglist ::= vtabarglist COMMA vtabarg", + /* 402 */ "vtabarg ::= vtabarg vtabargtoken", + /* 403 */ "anylist ::=", + /* 404 */ "anylist ::= anylist LP anylist RP", + /* 405 */ "anylist ::= anylist ANY", + /* 406 */ "with ::=", + /* 407 */ "windowdefn_list ::= windowdefn", + /* 408 */ "window ::= frame_opt", }; #endif /* NDEBUG */ -#if YYSTACKDEPTH<=0 +#if YYGROWABLESTACK /* ** Try to increase the size of the parser stack. Return the number ** of errors. Return 0 on success. */ static int yyGrowStack(yyParser *p){ + int oldSize = 1 + (int)(p->yystackEnd - p->yystack); int newSize; int idx; yyStackEntry *pNew; - newSize = p->yystksz*2 + 100; - idx = p->yytos ? (int)(p->yytos - p->yystack) : 0; - if( p->yystack==&p->yystk0 ){ - pNew = malloc(newSize*sizeof(pNew[0])); - if( pNew ) pNew[0] = p->yystk0; + newSize = oldSize*2 + 100; + idx = (int)(p->yytos - p->yystack); + if( p->yystack==p->yystk0 ){ + pNew = YYREALLOC(0, newSize*sizeof(pNew[0])); + if( pNew==0 ) return 1; + memcpy(pNew, p->yystack, oldSize*sizeof(pNew[0])); }else{ - pNew = realloc(p->yystack, newSize*sizeof(pNew[0])); + pNew = YYREALLOC(p->yystack, newSize*sizeof(pNew[0])); + if( pNew==0 ) return 1; } - if( pNew ){ - p->yystack = pNew; - p->yytos = &p->yystack[idx]; + p->yystack = pNew; + p->yytos = &p->yystack[idx]; #ifndef NDEBUG - if( yyTraceFILE ){ - fprintf(yyTraceFILE,"%sStack grows from %d to %d entries.\n", - yyTracePrompt, p->yystksz, newSize); - } -#endif - p->yystksz = newSize; + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sStack grows from %d to %d entries.\n", + yyTracePrompt, oldSize, newSize); } - return pNew==0; +#endif + p->yystackEnd = &p->yystack[newSize-1]; + return 0; } +#endif /* YYGROWABLESTACK */ + +#if !YYGROWABLESTACK +/* For builds that do no have a growable stack, yyGrowStack always +** returns an error. +*/ +# define yyGrowStack(X) 1 #endif /* Datatype of the argument to the memory allocated passed as the @@ -173406,24 +174798,14 @@ SQLITE_PRIVATE void sqlite3ParserInit(void *yypRawParser sqlite3ParserCTX_PDECL) #ifdef YYTRACKMAXSTACKDEPTH yypParser->yyhwm = 0; #endif -#if YYSTACKDEPTH<=0 - yypParser->yytos = NULL; - yypParser->yystack = NULL; - yypParser->yystksz = 0; - if( yyGrowStack(yypParser) ){ - yypParser->yystack = &yypParser->yystk0; - yypParser->yystksz = 1; - } -#endif + yypParser->yystack = yypParser->yystk0; + yypParser->yystackEnd = &yypParser->yystack[YYSTACKDEPTH-1]; #ifndef YYNOERRORRECOVERY yypParser->yyerrcnt = -1; #endif yypParser->yytos = yypParser->yystack; yypParser->yystack[0].stateno = 0; yypParser->yystack[0].major = 0; -#if YYSTACKDEPTH>0 - yypParser->yystackEnd = &yypParser->yystack[YYSTACKDEPTH-1]; -#endif } #ifndef sqlite3Parser_ENGINEALWAYSONSTACK @@ -173477,97 +174859,98 @@ static void yy_destructor( ** inside the C code. */ /********* Begin destructor definitions ***************************************/ - case 204: /* select */ - case 239: /* selectnowith */ - case 240: /* oneselect */ - case 252: /* values */ + case 205: /* select */ + case 240: /* selectnowith */ + case 241: /* oneselect */ + case 253: /* values */ + case 255: /* mvalues */ { -sqlite3SelectDelete(pParse->db, (yypminor->yy47)); +sqlite3SelectDelete(pParse->db, (yypminor->yy555)); } break; - case 216: /* term */ - case 217: /* expr */ - case 246: /* where_opt */ - case 248: /* having_opt */ - case 267: /* where_opt_ret */ - case 278: /* case_operand */ - case 280: /* case_else */ - case 283: /* vinto */ - case 290: /* when_clause */ - case 295: /* key_opt */ - case 311: /* filter_clause */ + case 217: /* term */ + case 218: /* expr */ + case 247: /* where_opt */ + case 249: /* having_opt */ + case 269: /* where_opt_ret */ + case 280: /* case_operand */ + case 282: /* case_else */ + case 285: /* vinto */ + case 292: /* when_clause */ + case 297: /* key_opt */ + case 314: /* filter_clause */ { -sqlite3ExprDelete(pParse->db, (yypminor->yy528)); +sqlite3ExprDelete(pParse->db, (yypminor->yy454)); } break; - case 221: /* eidlist_opt */ - case 231: /* sortlist */ - case 232: /* eidlist */ - case 244: /* selcollist */ - case 247: /* groupby_opt */ - case 249: /* orderby_opt */ - case 253: /* nexprlist */ - case 254: /* sclp */ - case 261: /* exprlist */ - case 268: /* setlist */ - case 277: /* paren_exprlist */ - case 279: /* case_exprlist */ - case 310: /* part_opt */ + case 222: /* eidlist_opt */ + case 232: /* sortlist */ + case 233: /* eidlist */ + case 245: /* selcollist */ + case 248: /* groupby_opt */ + case 250: /* orderby_opt */ + case 254: /* nexprlist */ + case 256: /* sclp */ + case 263: /* exprlist */ + case 270: /* setlist */ + case 279: /* paren_exprlist */ + case 281: /* case_exprlist */ + case 313: /* part_opt */ { -sqlite3ExprListDelete(pParse->db, (yypminor->yy322)); +sqlite3ExprListDelete(pParse->db, (yypminor->yy14)); } break; - case 238: /* fullname */ - case 245: /* from */ - case 256: /* seltablist */ - case 257: /* stl_prefix */ - case 262: /* xfullname */ + case 239: /* fullname */ + case 246: /* from */ + case 258: /* seltablist */ + case 259: /* stl_prefix */ + case 264: /* xfullname */ { -sqlite3SrcListDelete(pParse->db, (yypminor->yy131)); +sqlite3SrcListDelete(pParse->db, (yypminor->yy203)); } break; - case 241: /* wqlist */ + case 242: /* wqlist */ { -sqlite3WithDelete(pParse->db, (yypminor->yy521)); +sqlite3WithDelete(pParse->db, (yypminor->yy59)); } break; - case 251: /* window_clause */ - case 306: /* windowdefn_list */ + case 252: /* window_clause */ + case 309: /* windowdefn_list */ { -sqlite3WindowListDelete(pParse->db, (yypminor->yy41)); +sqlite3WindowListDelete(pParse->db, (yypminor->yy211)); } break; - case 263: /* idlist */ - case 270: /* idlist_opt */ + case 265: /* idlist */ + case 272: /* idlist_opt */ { -sqlite3IdListDelete(pParse->db, (yypminor->yy254)); +sqlite3IdListDelete(pParse->db, (yypminor->yy132)); } break; - case 273: /* filter_over */ - case 307: /* windowdefn */ - case 308: /* window */ - case 309: /* frame_opt */ - case 312: /* over_clause */ + case 275: /* filter_over */ + case 310: /* windowdefn */ + case 311: /* window */ + case 312: /* frame_opt */ + case 315: /* over_clause */ { -sqlite3WindowDelete(pParse->db, (yypminor->yy41)); +sqlite3WindowDelete(pParse->db, (yypminor->yy211)); } break; - case 286: /* trigger_cmd_list */ - case 291: /* trigger_cmd */ + case 288: /* trigger_cmd_list */ + case 293: /* trigger_cmd */ { -sqlite3DeleteTriggerStep(pParse->db, (yypminor->yy33)); +sqlite3DeleteTriggerStep(pParse->db, (yypminor->yy427)); } break; - case 288: /* trigger_event */ + case 290: /* trigger_event */ { -sqlite3IdListDelete(pParse->db, (yypminor->yy180).b); +sqlite3IdListDelete(pParse->db, (yypminor->yy286).b); } break; - case 314: /* frame_bound */ - case 315: /* frame_bound_s */ - case 316: /* frame_bound_e */ + case 317: /* frame_bound */ + case 318: /* frame_bound_s */ + case 319: /* frame_bound_e */ { -sqlite3ExprDelete(pParse->db, (yypminor->yy595).pExpr); +sqlite3ExprDelete(pParse->db, (yypminor->yy509).pExpr); } break; /********* End destructor definitions *****************************************/ @@ -173601,9 +174984,26 @@ static void yy_pop_parser_stack(yyParser *pParser){ */ SQLITE_PRIVATE void sqlite3ParserFinalize(void *p){ yyParser *pParser = (yyParser*)p; - while( pParser->yytos>pParser->yystack ) yy_pop_parser_stack(pParser); -#if YYSTACKDEPTH<=0 - if( pParser->yystack!=&pParser->yystk0 ) free(pParser->yystack); + + /* In-lined version of calling yy_pop_parser_stack() for each + ** element left in the stack */ + yyStackEntry *yytos = pParser->yytos; + while( yytos>pParser->yystack ){ +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sPopping %s\n", + yyTracePrompt, + yyTokenName[yytos->major]); + } +#endif + if( yytos->major>=YY_MIN_DSTRCTR ){ + yy_destructor(pParser, yytos->major, &yytos->minor); + } + yytos--; + } + +#if YYGROWABLESTACK + if( pParser->yystack!=pParser->yystk0 ) YYFREE(pParser->yystack); #endif } @@ -173786,7 +175186,7 @@ static void yyStackOverflow(yyParser *yypParser){ ** stack every overflows */ /******** Begin %stack_overflow code ******************************************/ - sqlite3ErrorMsg(pParse, "parser stack overflow"); + sqlite3OomFault(pParse->db); /******** End %stack_overflow code ********************************************/ sqlite3ParserARG_STORE /* Suppress warning about unused %extra_argument var */ sqlite3ParserCTX_STORE @@ -173830,25 +175230,19 @@ static void yy_shift( assert( yypParser->yyhwm == (int)(yypParser->yytos - yypParser->yystack) ); } #endif -#if YYSTACKDEPTH>0 - if( yypParser->yytos>yypParser->yystackEnd ){ - yypParser->yytos--; - yyStackOverflow(yypParser); - return; - } -#else - if( yypParser->yytos>=&yypParser->yystack[yypParser->yystksz] ){ + yytos = yypParser->yytos; + if( yytos>yypParser->yystackEnd ){ if( yyGrowStack(yypParser) ){ yypParser->yytos--; yyStackOverflow(yypParser); return; } + yytos = yypParser->yytos; + assert( yytos <= yypParser->yystackEnd ); } -#endif if( yyNewState > YY_MAX_SHIFT ){ yyNewState += YY_MIN_REDUCE - YY_MIN_SHIFTREDUCE; } - yytos = yypParser->yytos; yytos->stateno = yyNewState; yytos->major = yyMajor; yytos->minor.yy0 = yyMinor; @@ -173858,411 +175252,415 @@ static void yy_shift( /* For rule J, yyRuleInfoLhs[J] contains the symbol on the left-hand side ** of that rule */ static const YYCODETYPE yyRuleInfoLhs[] = { - 189, /* (0) explain ::= EXPLAIN */ - 189, /* (1) explain ::= EXPLAIN QUERY PLAN */ - 188, /* (2) cmdx ::= cmd */ - 190, /* (3) cmd ::= BEGIN transtype trans_opt */ - 191, /* (4) transtype ::= */ - 191, /* (5) transtype ::= DEFERRED */ - 191, /* (6) transtype ::= IMMEDIATE */ - 191, /* (7) transtype ::= EXCLUSIVE */ - 190, /* (8) cmd ::= COMMIT|END trans_opt */ - 190, /* (9) cmd ::= ROLLBACK trans_opt */ - 190, /* (10) cmd ::= SAVEPOINT nm */ - 190, /* (11) cmd ::= RELEASE savepoint_opt nm */ - 190, /* (12) cmd ::= ROLLBACK trans_opt TO savepoint_opt nm */ - 195, /* (13) create_table ::= createkw temp TABLE ifnotexists nm dbnm */ - 197, /* (14) createkw ::= CREATE */ - 199, /* (15) ifnotexists ::= */ - 199, /* (16) ifnotexists ::= IF NOT EXISTS */ - 198, /* (17) temp ::= TEMP */ - 198, /* (18) temp ::= */ - 196, /* (19) create_table_args ::= LP columnlist conslist_opt RP table_option_set */ - 196, /* (20) create_table_args ::= AS select */ - 203, /* (21) table_option_set ::= */ - 203, /* (22) table_option_set ::= table_option_set COMMA table_option */ - 205, /* (23) table_option ::= WITHOUT nm */ - 205, /* (24) table_option ::= nm */ - 206, /* (25) columnname ::= nm typetoken */ - 208, /* (26) typetoken ::= */ - 208, /* (27) typetoken ::= typename LP signed RP */ - 208, /* (28) typetoken ::= typename LP signed COMMA signed RP */ - 209, /* (29) typename ::= typename ID|STRING */ - 213, /* (30) scanpt ::= */ - 214, /* (31) scantok ::= */ - 215, /* (32) ccons ::= CONSTRAINT nm */ - 215, /* (33) ccons ::= DEFAULT scantok term */ - 215, /* (34) ccons ::= DEFAULT LP expr RP */ - 215, /* (35) ccons ::= DEFAULT PLUS scantok term */ - 215, /* (36) ccons ::= DEFAULT MINUS scantok term */ - 215, /* (37) ccons ::= DEFAULT scantok ID|INDEXED */ - 215, /* (38) ccons ::= NOT NULL onconf */ - 215, /* (39) ccons ::= PRIMARY KEY sortorder onconf autoinc */ - 215, /* (40) ccons ::= UNIQUE onconf */ - 215, /* (41) ccons ::= CHECK LP expr RP */ - 215, /* (42) ccons ::= REFERENCES nm eidlist_opt refargs */ - 215, /* (43) ccons ::= defer_subclause */ - 215, /* (44) ccons ::= COLLATE ID|STRING */ - 224, /* (45) generated ::= LP expr RP */ - 224, /* (46) generated ::= LP expr RP ID */ - 220, /* (47) autoinc ::= */ - 220, /* (48) autoinc ::= AUTOINCR */ - 222, /* (49) refargs ::= */ - 222, /* (50) refargs ::= refargs refarg */ - 225, /* (51) refarg ::= MATCH nm */ - 225, /* (52) refarg ::= ON INSERT refact */ - 225, /* (53) refarg ::= ON DELETE refact */ - 225, /* (54) refarg ::= ON UPDATE refact */ - 226, /* (55) refact ::= SET NULL */ - 226, /* (56) refact ::= SET DEFAULT */ - 226, /* (57) refact ::= CASCADE */ - 226, /* (58) refact ::= RESTRICT */ - 226, /* (59) refact ::= NO ACTION */ - 223, /* (60) defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */ - 223, /* (61) defer_subclause ::= DEFERRABLE init_deferred_pred_opt */ - 227, /* (62) init_deferred_pred_opt ::= */ - 227, /* (63) init_deferred_pred_opt ::= INITIALLY DEFERRED */ - 227, /* (64) init_deferred_pred_opt ::= INITIALLY IMMEDIATE */ - 202, /* (65) conslist_opt ::= */ - 229, /* (66) tconscomma ::= COMMA */ - 230, /* (67) tcons ::= CONSTRAINT nm */ - 230, /* (68) tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */ - 230, /* (69) tcons ::= UNIQUE LP sortlist RP onconf */ - 230, /* (70) tcons ::= CHECK LP expr RP onconf */ - 230, /* (71) tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */ - 233, /* (72) defer_subclause_opt ::= */ - 218, /* (73) onconf ::= */ - 218, /* (74) onconf ::= ON CONFLICT resolvetype */ - 234, /* (75) orconf ::= */ - 234, /* (76) orconf ::= OR resolvetype */ - 235, /* (77) resolvetype ::= IGNORE */ - 235, /* (78) resolvetype ::= REPLACE */ - 190, /* (79) cmd ::= DROP TABLE ifexists fullname */ - 237, /* (80) ifexists ::= IF EXISTS */ - 237, /* (81) ifexists ::= */ - 190, /* (82) cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */ - 190, /* (83) cmd ::= DROP VIEW ifexists fullname */ - 190, /* (84) cmd ::= select */ - 204, /* (85) select ::= WITH wqlist selectnowith */ - 204, /* (86) select ::= WITH RECURSIVE wqlist selectnowith */ - 204, /* (87) select ::= selectnowith */ - 239, /* (88) selectnowith ::= selectnowith multiselect_op oneselect */ - 242, /* (89) multiselect_op ::= UNION */ - 242, /* (90) multiselect_op ::= UNION ALL */ - 242, /* (91) multiselect_op ::= EXCEPT|INTERSECT */ - 240, /* (92) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */ - 240, /* (93) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */ - 252, /* (94) values ::= VALUES LP nexprlist RP */ - 252, /* (95) values ::= values COMMA LP nexprlist RP */ - 243, /* (96) distinct ::= DISTINCT */ - 243, /* (97) distinct ::= ALL */ - 243, /* (98) distinct ::= */ - 254, /* (99) sclp ::= */ - 244, /* (100) selcollist ::= sclp scanpt expr scanpt as */ - 244, /* (101) selcollist ::= sclp scanpt STAR */ - 244, /* (102) selcollist ::= sclp scanpt nm DOT STAR */ - 255, /* (103) as ::= AS nm */ - 255, /* (104) as ::= */ - 245, /* (105) from ::= */ - 245, /* (106) from ::= FROM seltablist */ - 257, /* (107) stl_prefix ::= seltablist joinop */ - 257, /* (108) stl_prefix ::= */ - 256, /* (109) seltablist ::= stl_prefix nm dbnm as on_using */ - 256, /* (110) seltablist ::= stl_prefix nm dbnm as indexed_by on_using */ - 256, /* (111) seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_using */ - 256, /* (112) seltablist ::= stl_prefix LP select RP as on_using */ - 256, /* (113) seltablist ::= stl_prefix LP seltablist RP as on_using */ - 200, /* (114) dbnm ::= */ - 200, /* (115) dbnm ::= DOT nm */ - 238, /* (116) fullname ::= nm */ - 238, /* (117) fullname ::= nm DOT nm */ - 262, /* (118) xfullname ::= nm */ - 262, /* (119) xfullname ::= nm DOT nm */ - 262, /* (120) xfullname ::= nm DOT nm AS nm */ - 262, /* (121) xfullname ::= nm AS nm */ - 258, /* (122) joinop ::= COMMA|JOIN */ - 258, /* (123) joinop ::= JOIN_KW JOIN */ - 258, /* (124) joinop ::= JOIN_KW nm JOIN */ - 258, /* (125) joinop ::= JOIN_KW nm nm JOIN */ - 259, /* (126) on_using ::= ON expr */ - 259, /* (127) on_using ::= USING LP idlist RP */ - 259, /* (128) on_using ::= */ - 264, /* (129) indexed_opt ::= */ - 260, /* (130) indexed_by ::= INDEXED BY nm */ - 260, /* (131) indexed_by ::= NOT INDEXED */ - 249, /* (132) orderby_opt ::= */ - 249, /* (133) orderby_opt ::= ORDER BY sortlist */ - 231, /* (134) sortlist ::= sortlist COMMA expr sortorder nulls */ - 231, /* (135) sortlist ::= expr sortorder nulls */ - 219, /* (136) sortorder ::= ASC */ - 219, /* (137) sortorder ::= DESC */ - 219, /* (138) sortorder ::= */ - 265, /* (139) nulls ::= NULLS FIRST */ - 265, /* (140) nulls ::= NULLS LAST */ - 265, /* (141) nulls ::= */ - 247, /* (142) groupby_opt ::= */ - 247, /* (143) groupby_opt ::= GROUP BY nexprlist */ - 248, /* (144) having_opt ::= */ - 248, /* (145) having_opt ::= HAVING expr */ - 250, /* (146) limit_opt ::= */ - 250, /* (147) limit_opt ::= LIMIT expr */ - 250, /* (148) limit_opt ::= LIMIT expr OFFSET expr */ - 250, /* (149) limit_opt ::= LIMIT expr COMMA expr */ - 190, /* (150) cmd ::= with DELETE FROM xfullname indexed_opt where_opt_ret */ - 246, /* (151) where_opt ::= */ - 246, /* (152) where_opt ::= WHERE expr */ - 267, /* (153) where_opt_ret ::= */ - 267, /* (154) where_opt_ret ::= WHERE expr */ - 267, /* (155) where_opt_ret ::= RETURNING selcollist */ - 267, /* (156) where_opt_ret ::= WHERE expr RETURNING selcollist */ - 190, /* (157) cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt_ret */ - 268, /* (158) setlist ::= setlist COMMA nm EQ expr */ - 268, /* (159) setlist ::= setlist COMMA LP idlist RP EQ expr */ - 268, /* (160) setlist ::= nm EQ expr */ - 268, /* (161) setlist ::= LP idlist RP EQ expr */ - 190, /* (162) cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */ - 190, /* (163) cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES returning */ - 271, /* (164) upsert ::= */ - 271, /* (165) upsert ::= RETURNING selcollist */ - 271, /* (166) upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert */ - 271, /* (167) upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert */ - 271, /* (168) upsert ::= ON CONFLICT DO NOTHING returning */ - 271, /* (169) upsert ::= ON CONFLICT DO UPDATE SET setlist where_opt returning */ - 272, /* (170) returning ::= RETURNING selcollist */ - 269, /* (171) insert_cmd ::= INSERT orconf */ - 269, /* (172) insert_cmd ::= REPLACE */ - 270, /* (173) idlist_opt ::= */ - 270, /* (174) idlist_opt ::= LP idlist RP */ - 263, /* (175) idlist ::= idlist COMMA nm */ - 263, /* (176) idlist ::= nm */ - 217, /* (177) expr ::= LP expr RP */ - 217, /* (178) expr ::= ID|INDEXED|JOIN_KW */ - 217, /* (179) expr ::= nm DOT nm */ - 217, /* (180) expr ::= nm DOT nm DOT nm */ - 216, /* (181) term ::= NULL|FLOAT|BLOB */ - 216, /* (182) term ::= STRING */ - 216, /* (183) term ::= INTEGER */ - 217, /* (184) expr ::= VARIABLE */ - 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 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 */ + 190, /* (0) explain ::= EXPLAIN */ + 190, /* (1) explain ::= EXPLAIN QUERY PLAN */ + 189, /* (2) cmdx ::= cmd */ + 191, /* (3) cmd ::= BEGIN transtype trans_opt */ + 192, /* (4) transtype ::= */ + 192, /* (5) transtype ::= DEFERRED */ + 192, /* (6) transtype ::= IMMEDIATE */ + 192, /* (7) transtype ::= EXCLUSIVE */ + 191, /* (8) cmd ::= COMMIT|END trans_opt */ + 191, /* (9) cmd ::= ROLLBACK trans_opt */ + 191, /* (10) cmd ::= SAVEPOINT nm */ + 191, /* (11) cmd ::= RELEASE savepoint_opt nm */ + 191, /* (12) cmd ::= ROLLBACK trans_opt TO savepoint_opt nm */ + 196, /* (13) create_table ::= createkw temp TABLE ifnotexists nm dbnm */ + 198, /* (14) createkw ::= CREATE */ + 200, /* (15) ifnotexists ::= */ + 200, /* (16) ifnotexists ::= IF NOT EXISTS */ + 199, /* (17) temp ::= TEMP */ + 199, /* (18) temp ::= */ + 197, /* (19) create_table_args ::= LP columnlist conslist_opt RP table_option_set */ + 197, /* (20) create_table_args ::= AS select */ + 204, /* (21) table_option_set ::= */ + 204, /* (22) table_option_set ::= table_option_set COMMA table_option */ + 206, /* (23) table_option ::= WITHOUT nm */ + 206, /* (24) table_option ::= nm */ + 207, /* (25) columnname ::= nm typetoken */ + 209, /* (26) typetoken ::= */ + 209, /* (27) typetoken ::= typename LP signed RP */ + 209, /* (28) typetoken ::= typename LP signed COMMA signed RP */ + 210, /* (29) typename ::= typename ID|STRING */ + 214, /* (30) scanpt ::= */ + 215, /* (31) scantok ::= */ + 216, /* (32) ccons ::= CONSTRAINT nm */ + 216, /* (33) ccons ::= DEFAULT scantok term */ + 216, /* (34) ccons ::= DEFAULT LP expr RP */ + 216, /* (35) ccons ::= DEFAULT PLUS scantok term */ + 216, /* (36) ccons ::= DEFAULT MINUS scantok term */ + 216, /* (37) ccons ::= DEFAULT scantok ID|INDEXED */ + 216, /* (38) ccons ::= NOT NULL onconf */ + 216, /* (39) ccons ::= PRIMARY KEY sortorder onconf autoinc */ + 216, /* (40) ccons ::= UNIQUE onconf */ + 216, /* (41) ccons ::= CHECK LP expr RP */ + 216, /* (42) ccons ::= REFERENCES nm eidlist_opt refargs */ + 216, /* (43) ccons ::= defer_subclause */ + 216, /* (44) ccons ::= COLLATE ID|STRING */ + 225, /* (45) generated ::= LP expr RP */ + 225, /* (46) generated ::= LP expr RP ID */ + 221, /* (47) autoinc ::= */ + 221, /* (48) autoinc ::= AUTOINCR */ + 223, /* (49) refargs ::= */ + 223, /* (50) refargs ::= refargs refarg */ + 226, /* (51) refarg ::= MATCH nm */ + 226, /* (52) refarg ::= ON INSERT refact */ + 226, /* (53) refarg ::= ON DELETE refact */ + 226, /* (54) refarg ::= ON UPDATE refact */ + 227, /* (55) refact ::= SET NULL */ + 227, /* (56) refact ::= SET DEFAULT */ + 227, /* (57) refact ::= CASCADE */ + 227, /* (58) refact ::= RESTRICT */ + 227, /* (59) refact ::= NO ACTION */ + 224, /* (60) defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */ + 224, /* (61) defer_subclause ::= DEFERRABLE init_deferred_pred_opt */ + 228, /* (62) init_deferred_pred_opt ::= */ + 228, /* (63) init_deferred_pred_opt ::= INITIALLY DEFERRED */ + 228, /* (64) init_deferred_pred_opt ::= INITIALLY IMMEDIATE */ + 203, /* (65) conslist_opt ::= */ + 230, /* (66) tconscomma ::= COMMA */ + 231, /* (67) tcons ::= CONSTRAINT nm */ + 231, /* (68) tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */ + 231, /* (69) tcons ::= UNIQUE LP sortlist RP onconf */ + 231, /* (70) tcons ::= CHECK LP expr RP onconf */ + 231, /* (71) tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */ + 234, /* (72) defer_subclause_opt ::= */ + 219, /* (73) onconf ::= */ + 219, /* (74) onconf ::= ON CONFLICT resolvetype */ + 235, /* (75) orconf ::= */ + 235, /* (76) orconf ::= OR resolvetype */ + 236, /* (77) resolvetype ::= IGNORE */ + 236, /* (78) resolvetype ::= REPLACE */ + 191, /* (79) cmd ::= DROP TABLE ifexists fullname */ + 238, /* (80) ifexists ::= IF EXISTS */ + 238, /* (81) ifexists ::= */ + 191, /* (82) cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */ + 191, /* (83) cmd ::= DROP VIEW ifexists fullname */ + 191, /* (84) cmd ::= select */ + 205, /* (85) select ::= WITH wqlist selectnowith */ + 205, /* (86) select ::= WITH RECURSIVE wqlist selectnowith */ + 205, /* (87) select ::= selectnowith */ + 240, /* (88) selectnowith ::= selectnowith multiselect_op oneselect */ + 243, /* (89) multiselect_op ::= UNION */ + 243, /* (90) multiselect_op ::= UNION ALL */ + 243, /* (91) multiselect_op ::= EXCEPT|INTERSECT */ + 241, /* (92) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */ + 241, /* (93) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */ + 253, /* (94) values ::= VALUES LP nexprlist RP */ + 241, /* (95) oneselect ::= mvalues */ + 255, /* (96) mvalues ::= values COMMA LP nexprlist RP */ + 255, /* (97) mvalues ::= mvalues COMMA LP nexprlist RP */ + 244, /* (98) distinct ::= DISTINCT */ + 244, /* (99) distinct ::= ALL */ + 244, /* (100) distinct ::= */ + 256, /* (101) sclp ::= */ + 245, /* (102) selcollist ::= sclp scanpt expr scanpt as */ + 245, /* (103) selcollist ::= sclp scanpt STAR */ + 245, /* (104) selcollist ::= sclp scanpt nm DOT STAR */ + 257, /* (105) as ::= AS nm */ + 257, /* (106) as ::= */ + 246, /* (107) from ::= */ + 246, /* (108) from ::= FROM seltablist */ + 259, /* (109) stl_prefix ::= seltablist joinop */ + 259, /* (110) stl_prefix ::= */ + 258, /* (111) seltablist ::= stl_prefix nm dbnm as on_using */ + 258, /* (112) seltablist ::= stl_prefix nm dbnm as indexed_by on_using */ + 258, /* (113) seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_using */ + 258, /* (114) seltablist ::= stl_prefix LP select RP as on_using */ + 258, /* (115) seltablist ::= stl_prefix LP seltablist RP as on_using */ + 201, /* (116) dbnm ::= */ + 201, /* (117) dbnm ::= DOT nm */ + 239, /* (118) fullname ::= nm */ + 239, /* (119) fullname ::= nm DOT nm */ + 264, /* (120) xfullname ::= nm */ + 264, /* (121) xfullname ::= nm DOT nm */ + 264, /* (122) xfullname ::= nm DOT nm AS nm */ + 264, /* (123) xfullname ::= nm AS nm */ + 260, /* (124) joinop ::= COMMA|JOIN */ + 260, /* (125) joinop ::= JOIN_KW JOIN */ + 260, /* (126) joinop ::= JOIN_KW nm JOIN */ + 260, /* (127) joinop ::= JOIN_KW nm nm JOIN */ + 261, /* (128) on_using ::= ON expr */ + 261, /* (129) on_using ::= USING LP idlist RP */ + 261, /* (130) on_using ::= */ + 266, /* (131) indexed_opt ::= */ + 262, /* (132) indexed_by ::= INDEXED BY nm */ + 262, /* (133) indexed_by ::= NOT INDEXED */ + 250, /* (134) orderby_opt ::= */ + 250, /* (135) orderby_opt ::= ORDER BY sortlist */ + 232, /* (136) sortlist ::= sortlist COMMA expr sortorder nulls */ + 232, /* (137) sortlist ::= expr sortorder nulls */ + 220, /* (138) sortorder ::= ASC */ + 220, /* (139) sortorder ::= DESC */ + 220, /* (140) sortorder ::= */ + 267, /* (141) nulls ::= NULLS FIRST */ + 267, /* (142) nulls ::= NULLS LAST */ + 267, /* (143) nulls ::= */ + 248, /* (144) groupby_opt ::= */ + 248, /* (145) groupby_opt ::= GROUP BY nexprlist */ + 249, /* (146) having_opt ::= */ + 249, /* (147) having_opt ::= HAVING expr */ + 251, /* (148) limit_opt ::= */ + 251, /* (149) limit_opt ::= LIMIT expr */ + 251, /* (150) limit_opt ::= LIMIT expr OFFSET expr */ + 251, /* (151) limit_opt ::= LIMIT expr COMMA expr */ + 191, /* (152) cmd ::= with DELETE FROM xfullname indexed_opt where_opt_ret */ + 247, /* (153) where_opt ::= */ + 247, /* (154) where_opt ::= WHERE expr */ + 269, /* (155) where_opt_ret ::= */ + 269, /* (156) where_opt_ret ::= WHERE expr */ + 269, /* (157) where_opt_ret ::= RETURNING selcollist */ + 269, /* (158) where_opt_ret ::= WHERE expr RETURNING selcollist */ + 191, /* (159) cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt_ret */ + 270, /* (160) setlist ::= setlist COMMA nm EQ expr */ + 270, /* (161) setlist ::= setlist COMMA LP idlist RP EQ expr */ + 270, /* (162) setlist ::= nm EQ expr */ + 270, /* (163) setlist ::= LP idlist RP EQ expr */ + 191, /* (164) cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */ + 191, /* (165) cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES returning */ + 273, /* (166) upsert ::= */ + 273, /* (167) upsert ::= RETURNING selcollist */ + 273, /* (168) upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert */ + 273, /* (169) upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert */ + 273, /* (170) upsert ::= ON CONFLICT DO NOTHING returning */ + 273, /* (171) upsert ::= ON CONFLICT DO UPDATE SET setlist where_opt returning */ + 274, /* (172) returning ::= RETURNING selcollist */ + 271, /* (173) insert_cmd ::= INSERT orconf */ + 271, /* (174) insert_cmd ::= REPLACE */ + 272, /* (175) idlist_opt ::= */ + 272, /* (176) idlist_opt ::= LP idlist RP */ + 265, /* (177) idlist ::= idlist COMMA nm */ + 265, /* (178) idlist ::= nm */ + 218, /* (179) expr ::= LP expr RP */ + 218, /* (180) expr ::= ID|INDEXED|JOIN_KW */ + 218, /* (181) expr ::= nm DOT nm */ + 218, /* (182) expr ::= nm DOT nm DOT nm */ + 217, /* (183) term ::= NULL|FLOAT|BLOB */ + 217, /* (184) term ::= STRING */ + 217, /* (185) term ::= INTEGER */ + 218, /* (186) expr ::= VARIABLE */ + 218, /* (187) expr ::= expr COLLATE ID|STRING */ + 218, /* (188) expr ::= CAST LP expr AS typetoken RP */ + 218, /* (189) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP */ + 218, /* (190) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP */ + 218, /* (191) expr ::= ID|INDEXED|JOIN_KW LP STAR RP */ + 218, /* (192) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP filter_over */ + 218, /* (193) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP filter_over */ + 218, /* (194) expr ::= ID|INDEXED|JOIN_KW LP STAR RP filter_over */ + 217, /* (195) term ::= CTIME_KW */ + 218, /* (196) expr ::= LP nexprlist COMMA expr RP */ + 218, /* (197) expr ::= expr AND expr */ + 218, /* (198) expr ::= expr OR expr */ + 218, /* (199) expr ::= expr LT|GT|GE|LE expr */ + 218, /* (200) expr ::= expr EQ|NE expr */ + 218, /* (201) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ + 218, /* (202) expr ::= expr PLUS|MINUS expr */ + 218, /* (203) expr ::= expr STAR|SLASH|REM expr */ + 218, /* (204) expr ::= expr CONCAT expr */ + 276, /* (205) likeop ::= NOT LIKE_KW|MATCH */ + 218, /* (206) expr ::= expr likeop expr */ + 218, /* (207) expr ::= expr likeop expr ESCAPE expr */ + 218, /* (208) expr ::= expr ISNULL|NOTNULL */ + 218, /* (209) expr ::= expr NOT NULL */ + 218, /* (210) expr ::= expr IS expr */ + 218, /* (211) expr ::= expr IS NOT expr */ + 218, /* (212) expr ::= expr IS NOT DISTINCT FROM expr */ + 218, /* (213) expr ::= expr IS DISTINCT FROM expr */ + 218, /* (214) expr ::= NOT expr */ + 218, /* (215) expr ::= BITNOT expr */ + 218, /* (216) expr ::= PLUS|MINUS expr */ + 218, /* (217) expr ::= expr PTR expr */ + 277, /* (218) between_op ::= BETWEEN */ + 277, /* (219) between_op ::= NOT BETWEEN */ + 218, /* (220) expr ::= expr between_op expr AND expr */ + 278, /* (221) in_op ::= IN */ + 278, /* (222) in_op ::= NOT IN */ + 218, /* (223) expr ::= expr in_op LP exprlist RP */ + 218, /* (224) expr ::= LP select RP */ + 218, /* (225) expr ::= expr in_op LP select RP */ + 218, /* (226) expr ::= expr in_op nm dbnm paren_exprlist */ + 218, /* (227) expr ::= EXISTS LP select RP */ + 218, /* (228) expr ::= CASE case_operand case_exprlist case_else END */ + 281, /* (229) case_exprlist ::= case_exprlist WHEN expr THEN expr */ + 281, /* (230) case_exprlist ::= WHEN expr THEN expr */ + 282, /* (231) case_else ::= ELSE expr */ + 282, /* (232) case_else ::= */ + 280, /* (233) case_operand ::= */ + 263, /* (234) exprlist ::= */ + 254, /* (235) nexprlist ::= nexprlist COMMA expr */ + 254, /* (236) nexprlist ::= expr */ + 279, /* (237) paren_exprlist ::= */ + 279, /* (238) paren_exprlist ::= LP exprlist RP */ + 191, /* (239) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ + 283, /* (240) uniqueflag ::= UNIQUE */ + 283, /* (241) uniqueflag ::= */ + 222, /* (242) eidlist_opt ::= */ + 222, /* (243) eidlist_opt ::= LP eidlist RP */ + 233, /* (244) eidlist ::= eidlist COMMA nm collate sortorder */ + 233, /* (245) eidlist ::= nm collate sortorder */ + 284, /* (246) collate ::= */ + 284, /* (247) collate ::= COLLATE ID|STRING */ + 191, /* (248) cmd ::= DROP INDEX ifexists fullname */ + 191, /* (249) cmd ::= VACUUM vinto */ + 191, /* (250) cmd ::= VACUUM nm vinto */ + 285, /* (251) vinto ::= INTO expr */ + 285, /* (252) vinto ::= */ + 191, /* (253) cmd ::= PRAGMA nm dbnm */ + 191, /* (254) cmd ::= PRAGMA nm dbnm EQ nmnum */ + 191, /* (255) cmd ::= PRAGMA nm dbnm LP nmnum RP */ + 191, /* (256) cmd ::= PRAGMA nm dbnm EQ minus_num */ + 191, /* (257) cmd ::= PRAGMA nm dbnm LP minus_num RP */ + 212, /* (258) plus_num ::= PLUS INTEGER|FLOAT */ + 213, /* (259) minus_num ::= MINUS INTEGER|FLOAT */ + 191, /* (260) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ + 287, /* (261) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ + 289, /* (262) trigger_time ::= BEFORE|AFTER */ + 289, /* (263) trigger_time ::= INSTEAD OF */ + 289, /* (264) trigger_time ::= */ + 290, /* (265) trigger_event ::= DELETE|INSERT */ + 290, /* (266) trigger_event ::= UPDATE */ + 290, /* (267) trigger_event ::= UPDATE OF idlist */ + 292, /* (268) when_clause ::= */ + 292, /* (269) when_clause ::= WHEN expr */ + 288, /* (270) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ + 288, /* (271) trigger_cmd_list ::= trigger_cmd SEMI */ + 294, /* (272) trnm ::= nm DOT nm */ + 295, /* (273) tridxby ::= INDEXED BY nm */ + 295, /* (274) tridxby ::= NOT INDEXED */ + 293, /* (275) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */ + 293, /* (276) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */ + 293, /* (277) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ + 293, /* (278) trigger_cmd ::= scanpt select scanpt */ + 218, /* (279) expr ::= RAISE LP IGNORE RP */ + 218, /* (280) expr ::= RAISE LP raisetype COMMA nm RP */ + 237, /* (281) raisetype ::= ROLLBACK */ + 237, /* (282) raisetype ::= ABORT */ + 237, /* (283) raisetype ::= FAIL */ + 191, /* (284) cmd ::= DROP TRIGGER ifexists fullname */ + 191, /* (285) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ + 191, /* (286) cmd ::= DETACH database_kw_opt expr */ + 297, /* (287) key_opt ::= */ + 297, /* (288) key_opt ::= KEY expr */ + 191, /* (289) cmd ::= REINDEX */ + 191, /* (290) cmd ::= REINDEX nm dbnm */ + 191, /* (291) cmd ::= ANALYZE */ + 191, /* (292) cmd ::= ANALYZE nm dbnm */ + 191, /* (293) cmd ::= ALTER TABLE fullname RENAME TO nm */ + 191, /* (294) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ + 191, /* (295) cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */ + 298, /* (296) add_column_fullname ::= fullname */ + 191, /* (297) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ + 191, /* (298) cmd ::= create_vtab */ + 191, /* (299) cmd ::= create_vtab LP vtabarglist RP */ + 300, /* (300) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ + 302, /* (301) vtabarg ::= */ + 303, /* (302) vtabargtoken ::= ANY */ + 303, /* (303) vtabargtoken ::= lp anylist RP */ + 304, /* (304) lp ::= LP */ + 268, /* (305) with ::= WITH wqlist */ + 268, /* (306) with ::= WITH RECURSIVE wqlist */ + 307, /* (307) wqas ::= AS */ + 307, /* (308) wqas ::= AS MATERIALIZED */ + 307, /* (309) wqas ::= AS NOT MATERIALIZED */ + 306, /* (310) wqitem ::= withnm eidlist_opt wqas LP select RP */ + 308, /* (311) withnm ::= nm */ + 242, /* (312) wqlist ::= wqitem */ + 242, /* (313) wqlist ::= wqlist COMMA wqitem */ + 309, /* (314) windowdefn_list ::= windowdefn_list COMMA windowdefn */ + 310, /* (315) windowdefn ::= nm AS LP window RP */ + 311, /* (316) window ::= PARTITION BY nexprlist orderby_opt frame_opt */ + 311, /* (317) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ + 311, /* (318) window ::= ORDER BY sortlist frame_opt */ + 311, /* (319) window ::= nm ORDER BY sortlist frame_opt */ + 311, /* (320) window ::= nm frame_opt */ + 312, /* (321) frame_opt ::= */ + 312, /* (322) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ + 312, /* (323) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ + 316, /* (324) range_or_rows ::= RANGE|ROWS|GROUPS */ + 318, /* (325) frame_bound_s ::= frame_bound */ + 318, /* (326) frame_bound_s ::= UNBOUNDED PRECEDING */ + 319, /* (327) frame_bound_e ::= frame_bound */ + 319, /* (328) frame_bound_e ::= UNBOUNDED FOLLOWING */ + 317, /* (329) frame_bound ::= expr PRECEDING|FOLLOWING */ + 317, /* (330) frame_bound ::= CURRENT ROW */ + 320, /* (331) frame_exclude_opt ::= */ + 320, /* (332) frame_exclude_opt ::= EXCLUDE frame_exclude */ + 321, /* (333) frame_exclude ::= NO OTHERS */ + 321, /* (334) frame_exclude ::= CURRENT ROW */ + 321, /* (335) frame_exclude ::= GROUP|TIES */ + 252, /* (336) window_clause ::= WINDOW windowdefn_list */ + 275, /* (337) filter_over ::= filter_clause over_clause */ + 275, /* (338) filter_over ::= over_clause */ + 275, /* (339) filter_over ::= filter_clause */ + 315, /* (340) over_clause ::= OVER LP window RP */ + 315, /* (341) over_clause ::= OVER nm */ + 314, /* (342) filter_clause ::= FILTER LP WHERE expr RP */ + 217, /* (343) term ::= QNUMBER */ + 186, /* (344) input ::= cmdlist */ + 187, /* (345) cmdlist ::= cmdlist ecmd */ + 187, /* (346) cmdlist ::= ecmd */ + 188, /* (347) ecmd ::= SEMI */ + 188, /* (348) ecmd ::= cmdx SEMI */ + 188, /* (349) ecmd ::= explain cmdx SEMI */ + 193, /* (350) trans_opt ::= */ + 193, /* (351) trans_opt ::= TRANSACTION */ + 193, /* (352) trans_opt ::= TRANSACTION nm */ + 195, /* (353) savepoint_opt ::= SAVEPOINT */ + 195, /* (354) savepoint_opt ::= */ + 191, /* (355) cmd ::= create_table create_table_args */ + 204, /* (356) table_option_set ::= table_option */ + 202, /* (357) columnlist ::= columnlist COMMA columnname carglist */ + 202, /* (358) columnlist ::= columnname carglist */ + 194, /* (359) nm ::= ID|INDEXED|JOIN_KW */ + 194, /* (360) nm ::= STRING */ + 209, /* (361) typetoken ::= typename */ + 210, /* (362) typename ::= ID|STRING */ + 211, /* (363) signed ::= plus_num */ + 211, /* (364) signed ::= minus_num */ + 208, /* (365) carglist ::= carglist ccons */ + 208, /* (366) carglist ::= */ + 216, /* (367) ccons ::= NULL onconf */ + 216, /* (368) ccons ::= GENERATED ALWAYS AS generated */ + 216, /* (369) ccons ::= AS generated */ + 203, /* (370) conslist_opt ::= COMMA conslist */ + 229, /* (371) conslist ::= conslist tconscomma tcons */ + 229, /* (372) conslist ::= tcons */ + 230, /* (373) tconscomma ::= */ + 234, /* (374) defer_subclause_opt ::= defer_subclause */ + 236, /* (375) resolvetype ::= raisetype */ + 240, /* (376) selectnowith ::= oneselect */ + 241, /* (377) oneselect ::= values */ + 256, /* (378) sclp ::= selcollist COMMA */ + 257, /* (379) as ::= ID|STRING */ + 266, /* (380) indexed_opt ::= indexed_by */ + 274, /* (381) returning ::= */ + 218, /* (382) expr ::= term */ + 276, /* (383) likeop ::= LIKE_KW|MATCH */ + 280, /* (384) case_operand ::= expr */ + 263, /* (385) exprlist ::= nexprlist */ + 286, /* (386) nmnum ::= plus_num */ + 286, /* (387) nmnum ::= nm */ + 286, /* (388) nmnum ::= ON */ + 286, /* (389) nmnum ::= DELETE */ + 286, /* (390) nmnum ::= DEFAULT */ + 212, /* (391) plus_num ::= INTEGER|FLOAT */ + 291, /* (392) foreach_clause ::= */ + 291, /* (393) foreach_clause ::= FOR EACH ROW */ + 294, /* (394) trnm ::= nm */ + 295, /* (395) tridxby ::= */ + 296, /* (396) database_kw_opt ::= DATABASE */ + 296, /* (397) database_kw_opt ::= */ + 299, /* (398) kwcolumn_opt ::= */ + 299, /* (399) kwcolumn_opt ::= COLUMNKW */ + 301, /* (400) vtabarglist ::= vtabarg */ + 301, /* (401) vtabarglist ::= vtabarglist COMMA vtabarg */ + 302, /* (402) vtabarg ::= vtabarg vtabargtoken */ + 305, /* (403) anylist ::= */ + 305, /* (404) anylist ::= anylist LP anylist RP */ + 305, /* (405) anylist ::= anylist ANY */ + 268, /* (406) with ::= */ + 309, /* (407) windowdefn_list ::= windowdefn */ + 311, /* (408) window ::= frame_opt */ }; /* For rule J, yyRuleInfoNRhs[J] contains the negative of the number @@ -174363,316 +175761,320 @@ static const signed char yyRuleInfoNRhs[] = { -9, /* (92) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */ -10, /* (93) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */ -4, /* (94) values ::= VALUES LP nexprlist RP */ - -5, /* (95) values ::= values COMMA LP nexprlist RP */ - -1, /* (96) distinct ::= DISTINCT */ - -1, /* (97) distinct ::= ALL */ - 0, /* (98) distinct ::= */ - 0, /* (99) sclp ::= */ - -5, /* (100) selcollist ::= sclp scanpt expr scanpt as */ - -3, /* (101) selcollist ::= sclp scanpt STAR */ - -5, /* (102) selcollist ::= sclp scanpt nm DOT STAR */ - -2, /* (103) as ::= AS nm */ - 0, /* (104) as ::= */ - 0, /* (105) from ::= */ - -2, /* (106) from ::= FROM seltablist */ - -2, /* (107) stl_prefix ::= seltablist joinop */ - 0, /* (108) stl_prefix ::= */ - -5, /* (109) seltablist ::= stl_prefix nm dbnm as on_using */ - -6, /* (110) seltablist ::= stl_prefix nm dbnm as indexed_by on_using */ - -8, /* (111) seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_using */ - -6, /* (112) seltablist ::= stl_prefix LP select RP as on_using */ - -6, /* (113) seltablist ::= stl_prefix LP seltablist RP as on_using */ - 0, /* (114) dbnm ::= */ - -2, /* (115) dbnm ::= DOT nm */ - -1, /* (116) fullname ::= nm */ - -3, /* (117) fullname ::= nm DOT nm */ - -1, /* (118) xfullname ::= nm */ - -3, /* (119) xfullname ::= nm DOT nm */ - -5, /* (120) xfullname ::= nm DOT nm AS nm */ - -3, /* (121) xfullname ::= nm AS nm */ - -1, /* (122) joinop ::= COMMA|JOIN */ - -2, /* (123) joinop ::= JOIN_KW JOIN */ - -3, /* (124) joinop ::= JOIN_KW nm JOIN */ - -4, /* (125) joinop ::= JOIN_KW nm nm JOIN */ - -2, /* (126) on_using ::= ON expr */ - -4, /* (127) on_using ::= USING LP idlist RP */ - 0, /* (128) on_using ::= */ - 0, /* (129) indexed_opt ::= */ - -3, /* (130) indexed_by ::= INDEXED BY nm */ - -2, /* (131) indexed_by ::= NOT INDEXED */ - 0, /* (132) orderby_opt ::= */ - -3, /* (133) orderby_opt ::= ORDER BY sortlist */ - -5, /* (134) sortlist ::= sortlist COMMA expr sortorder nulls */ - -3, /* (135) sortlist ::= expr sortorder nulls */ - -1, /* (136) sortorder ::= ASC */ - -1, /* (137) sortorder ::= DESC */ - 0, /* (138) sortorder ::= */ - -2, /* (139) nulls ::= NULLS FIRST */ - -2, /* (140) nulls ::= NULLS LAST */ - 0, /* (141) nulls ::= */ - 0, /* (142) groupby_opt ::= */ - -3, /* (143) groupby_opt ::= GROUP BY nexprlist */ - 0, /* (144) having_opt ::= */ - -2, /* (145) having_opt ::= HAVING expr */ - 0, /* (146) limit_opt ::= */ - -2, /* (147) limit_opt ::= LIMIT expr */ - -4, /* (148) limit_opt ::= LIMIT expr OFFSET expr */ - -4, /* (149) limit_opt ::= LIMIT expr COMMA expr */ - -6, /* (150) cmd ::= with DELETE FROM xfullname indexed_opt where_opt_ret */ - 0, /* (151) where_opt ::= */ - -2, /* (152) where_opt ::= WHERE expr */ - 0, /* (153) where_opt_ret ::= */ - -2, /* (154) where_opt_ret ::= WHERE expr */ - -2, /* (155) where_opt_ret ::= RETURNING selcollist */ - -4, /* (156) where_opt_ret ::= WHERE expr RETURNING selcollist */ - -9, /* (157) cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt_ret */ - -5, /* (158) setlist ::= setlist COMMA nm EQ expr */ - -7, /* (159) setlist ::= setlist COMMA LP idlist RP EQ expr */ - -3, /* (160) setlist ::= nm EQ expr */ - -5, /* (161) setlist ::= LP idlist RP EQ expr */ - -7, /* (162) cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */ - -8, /* (163) cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES returning */ - 0, /* (164) upsert ::= */ - -2, /* (165) upsert ::= RETURNING selcollist */ - -12, /* (166) upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert */ - -9, /* (167) upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert */ - -5, /* (168) upsert ::= ON CONFLICT DO NOTHING returning */ - -8, /* (169) upsert ::= ON CONFLICT DO UPDATE SET setlist where_opt returning */ - -2, /* (170) returning ::= RETURNING selcollist */ - -2, /* (171) insert_cmd ::= INSERT orconf */ - -1, /* (172) insert_cmd ::= REPLACE */ - 0, /* (173) idlist_opt ::= */ - -3, /* (174) idlist_opt ::= LP idlist RP */ - -3, /* (175) idlist ::= idlist COMMA nm */ - -1, /* (176) idlist ::= nm */ - -3, /* (177) expr ::= LP expr RP */ - -1, /* (178) expr ::= ID|INDEXED|JOIN_KW */ - -3, /* (179) expr ::= nm DOT nm */ - -5, /* (180) expr ::= nm DOT nm DOT nm */ - -1, /* (181) term ::= NULL|FLOAT|BLOB */ - -1, /* (182) term ::= STRING */ - -1, /* (183) term ::= INTEGER */ - -1, /* (184) expr ::= VARIABLE */ - -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 */ - -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 */ + -1, /* (95) oneselect ::= mvalues */ + -5, /* (96) mvalues ::= values COMMA LP nexprlist RP */ + -5, /* (97) mvalues ::= mvalues COMMA LP nexprlist RP */ + -1, /* (98) distinct ::= DISTINCT */ + -1, /* (99) distinct ::= ALL */ + 0, /* (100) distinct ::= */ + 0, /* (101) sclp ::= */ + -5, /* (102) selcollist ::= sclp scanpt expr scanpt as */ + -3, /* (103) selcollist ::= sclp scanpt STAR */ + -5, /* (104) selcollist ::= sclp scanpt nm DOT STAR */ + -2, /* (105) as ::= AS nm */ + 0, /* (106) as ::= */ + 0, /* (107) from ::= */ + -2, /* (108) from ::= FROM seltablist */ + -2, /* (109) stl_prefix ::= seltablist joinop */ + 0, /* (110) stl_prefix ::= */ + -5, /* (111) seltablist ::= stl_prefix nm dbnm as on_using */ + -6, /* (112) seltablist ::= stl_prefix nm dbnm as indexed_by on_using */ + -8, /* (113) seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_using */ + -6, /* (114) seltablist ::= stl_prefix LP select RP as on_using */ + -6, /* (115) seltablist ::= stl_prefix LP seltablist RP as on_using */ + 0, /* (116) dbnm ::= */ + -2, /* (117) dbnm ::= DOT nm */ + -1, /* (118) fullname ::= nm */ + -3, /* (119) fullname ::= nm DOT nm */ + -1, /* (120) xfullname ::= nm */ + -3, /* (121) xfullname ::= nm DOT nm */ + -5, /* (122) xfullname ::= nm DOT nm AS nm */ + -3, /* (123) xfullname ::= nm AS nm */ + -1, /* (124) joinop ::= COMMA|JOIN */ + -2, /* (125) joinop ::= JOIN_KW JOIN */ + -3, /* (126) joinop ::= JOIN_KW nm JOIN */ + -4, /* (127) joinop ::= JOIN_KW nm nm JOIN */ + -2, /* (128) on_using ::= ON expr */ + -4, /* (129) on_using ::= USING LP idlist RP */ + 0, /* (130) on_using ::= */ + 0, /* (131) indexed_opt ::= */ + -3, /* (132) indexed_by ::= INDEXED BY nm */ + -2, /* (133) indexed_by ::= NOT INDEXED */ + 0, /* (134) orderby_opt ::= */ + -3, /* (135) orderby_opt ::= ORDER BY sortlist */ + -5, /* (136) sortlist ::= sortlist COMMA expr sortorder nulls */ + -3, /* (137) sortlist ::= expr sortorder nulls */ + -1, /* (138) sortorder ::= ASC */ + -1, /* (139) sortorder ::= DESC */ + 0, /* (140) sortorder ::= */ + -2, /* (141) nulls ::= NULLS FIRST */ + -2, /* (142) nulls ::= NULLS LAST */ + 0, /* (143) nulls ::= */ + 0, /* (144) groupby_opt ::= */ + -3, /* (145) groupby_opt ::= GROUP BY nexprlist */ + 0, /* (146) having_opt ::= */ + -2, /* (147) having_opt ::= HAVING expr */ + 0, /* (148) limit_opt ::= */ + -2, /* (149) limit_opt ::= LIMIT expr */ + -4, /* (150) limit_opt ::= LIMIT expr OFFSET expr */ + -4, /* (151) limit_opt ::= LIMIT expr COMMA expr */ + -6, /* (152) cmd ::= with DELETE FROM xfullname indexed_opt where_opt_ret */ + 0, /* (153) where_opt ::= */ + -2, /* (154) where_opt ::= WHERE expr */ + 0, /* (155) where_opt_ret ::= */ + -2, /* (156) where_opt_ret ::= WHERE expr */ + -2, /* (157) where_opt_ret ::= RETURNING selcollist */ + -4, /* (158) where_opt_ret ::= WHERE expr RETURNING selcollist */ + -9, /* (159) cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt_ret */ + -5, /* (160) setlist ::= setlist COMMA nm EQ expr */ + -7, /* (161) setlist ::= setlist COMMA LP idlist RP EQ expr */ + -3, /* (162) setlist ::= nm EQ expr */ + -5, /* (163) setlist ::= LP idlist RP EQ expr */ + -7, /* (164) cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */ + -8, /* (165) cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES returning */ + 0, /* (166) upsert ::= */ + -2, /* (167) upsert ::= RETURNING selcollist */ + -12, /* (168) upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert */ + -9, /* (169) upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert */ + -5, /* (170) upsert ::= ON CONFLICT DO NOTHING returning */ + -8, /* (171) upsert ::= ON CONFLICT DO UPDATE SET setlist where_opt returning */ + -2, /* (172) returning ::= RETURNING selcollist */ + -2, /* (173) insert_cmd ::= INSERT orconf */ + -1, /* (174) insert_cmd ::= REPLACE */ + 0, /* (175) idlist_opt ::= */ + -3, /* (176) idlist_opt ::= LP idlist RP */ + -3, /* (177) idlist ::= idlist COMMA nm */ + -1, /* (178) idlist ::= nm */ + -3, /* (179) expr ::= LP expr RP */ + -1, /* (180) expr ::= ID|INDEXED|JOIN_KW */ + -3, /* (181) expr ::= nm DOT nm */ + -5, /* (182) expr ::= nm DOT nm DOT nm */ + -1, /* (183) term ::= NULL|FLOAT|BLOB */ + -1, /* (184) term ::= STRING */ + -1, /* (185) term ::= INTEGER */ + -1, /* (186) expr ::= VARIABLE */ + -3, /* (187) expr ::= expr COLLATE ID|STRING */ + -6, /* (188) expr ::= CAST LP expr AS typetoken RP */ + -5, /* (189) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP */ + -8, /* (190) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP */ + -4, /* (191) expr ::= ID|INDEXED|JOIN_KW LP STAR RP */ + -6, /* (192) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP filter_over */ + -9, /* (193) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP filter_over */ + -5, /* (194) expr ::= ID|INDEXED|JOIN_KW LP STAR RP filter_over */ + -1, /* (195) term ::= CTIME_KW */ + -5, /* (196) expr ::= LP nexprlist COMMA expr RP */ + -3, /* (197) expr ::= expr AND expr */ + -3, /* (198) expr ::= expr OR expr */ + -3, /* (199) expr ::= expr LT|GT|GE|LE expr */ + -3, /* (200) expr ::= expr EQ|NE expr */ + -3, /* (201) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ + -3, /* (202) expr ::= expr PLUS|MINUS expr */ + -3, /* (203) expr ::= expr STAR|SLASH|REM expr */ + -3, /* (204) expr ::= expr CONCAT expr */ + -2, /* (205) likeop ::= NOT LIKE_KW|MATCH */ + -3, /* (206) expr ::= expr likeop expr */ + -5, /* (207) expr ::= expr likeop expr ESCAPE expr */ + -2, /* (208) expr ::= expr ISNULL|NOTNULL */ + -3, /* (209) expr ::= expr NOT NULL */ + -3, /* (210) expr ::= expr IS expr */ + -4, /* (211) expr ::= expr IS NOT expr */ + -6, /* (212) expr ::= expr IS NOT DISTINCT FROM expr */ + -5, /* (213) expr ::= expr IS DISTINCT FROM expr */ + -2, /* (214) expr ::= NOT expr */ + -2, /* (215) expr ::= BITNOT expr */ + -2, /* (216) expr ::= PLUS|MINUS expr */ + -3, /* (217) expr ::= expr PTR expr */ + -1, /* (218) between_op ::= BETWEEN */ + -2, /* (219) between_op ::= NOT BETWEEN */ + -5, /* (220) expr ::= expr between_op expr AND expr */ + -1, /* (221) in_op ::= IN */ + -2, /* (222) in_op ::= NOT IN */ + -5, /* (223) expr ::= expr in_op LP exprlist RP */ + -3, /* (224) expr ::= LP select RP */ + -5, /* (225) expr ::= expr in_op LP select RP */ + -5, /* (226) expr ::= expr in_op nm dbnm paren_exprlist */ + -4, /* (227) expr ::= EXISTS LP select RP */ + -5, /* (228) expr ::= CASE case_operand case_exprlist case_else END */ + -5, /* (229) case_exprlist ::= case_exprlist WHEN expr THEN expr */ + -4, /* (230) case_exprlist ::= WHEN expr THEN expr */ + -2, /* (231) case_else ::= ELSE expr */ + 0, /* (232) case_else ::= */ + 0, /* (233) case_operand ::= */ + 0, /* (234) exprlist ::= */ + -3, /* (235) nexprlist ::= nexprlist COMMA expr */ + -1, /* (236) nexprlist ::= expr */ + 0, /* (237) paren_exprlist ::= */ + -3, /* (238) paren_exprlist ::= LP exprlist RP */ + -12, /* (239) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ + -1, /* (240) uniqueflag ::= UNIQUE */ + 0, /* (241) uniqueflag ::= */ + 0, /* (242) eidlist_opt ::= */ + -3, /* (243) eidlist_opt ::= LP eidlist RP */ + -5, /* (244) eidlist ::= eidlist COMMA nm collate sortorder */ + -3, /* (245) eidlist ::= nm collate sortorder */ + 0, /* (246) collate ::= */ + -2, /* (247) collate ::= COLLATE ID|STRING */ + -4, /* (248) cmd ::= DROP INDEX ifexists fullname */ + -2, /* (249) cmd ::= VACUUM vinto */ + -3, /* (250) cmd ::= VACUUM nm vinto */ + -2, /* (251) vinto ::= INTO expr */ + 0, /* (252) vinto ::= */ + -3, /* (253) cmd ::= PRAGMA nm dbnm */ + -5, /* (254) cmd ::= PRAGMA nm dbnm EQ nmnum */ + -6, /* (255) cmd ::= PRAGMA nm dbnm LP nmnum RP */ + -5, /* (256) cmd ::= PRAGMA nm dbnm EQ minus_num */ + -6, /* (257) cmd ::= PRAGMA nm dbnm LP minus_num RP */ + -2, /* (258) plus_num ::= PLUS INTEGER|FLOAT */ + -2, /* (259) minus_num ::= MINUS INTEGER|FLOAT */ + -5, /* (260) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ + -11, /* (261) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ + -1, /* (262) trigger_time ::= BEFORE|AFTER */ + -2, /* (263) trigger_time ::= INSTEAD OF */ + 0, /* (264) trigger_time ::= */ + -1, /* (265) trigger_event ::= DELETE|INSERT */ + -1, /* (266) trigger_event ::= UPDATE */ + -3, /* (267) trigger_event ::= UPDATE OF idlist */ + 0, /* (268) when_clause ::= */ + -2, /* (269) when_clause ::= WHEN expr */ + -3, /* (270) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ + -2, /* (271) trigger_cmd_list ::= trigger_cmd SEMI */ + -3, /* (272) trnm ::= nm DOT nm */ + -3, /* (273) tridxby ::= INDEXED BY nm */ + -2, /* (274) tridxby ::= NOT INDEXED */ + -9, /* (275) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */ + -8, /* (276) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */ + -6, /* (277) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ + -3, /* (278) trigger_cmd ::= scanpt select scanpt */ + -4, /* (279) expr ::= RAISE LP IGNORE RP */ + -6, /* (280) expr ::= RAISE LP raisetype COMMA nm RP */ + -1, /* (281) raisetype ::= ROLLBACK */ + -1, /* (282) raisetype ::= ABORT */ + -1, /* (283) raisetype ::= FAIL */ + -4, /* (284) cmd ::= DROP TRIGGER ifexists fullname */ + -6, /* (285) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ + -3, /* (286) cmd ::= DETACH database_kw_opt expr */ + 0, /* (287) key_opt ::= */ + -2, /* (288) key_opt ::= KEY expr */ + -1, /* (289) cmd ::= REINDEX */ + -3, /* (290) cmd ::= REINDEX nm dbnm */ + -1, /* (291) cmd ::= ANALYZE */ + -3, /* (292) cmd ::= ANALYZE nm dbnm */ + -6, /* (293) cmd ::= ALTER TABLE fullname RENAME TO nm */ + -7, /* (294) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ + -6, /* (295) cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */ + -1, /* (296) add_column_fullname ::= fullname */ + -8, /* (297) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ + -1, /* (298) cmd ::= create_vtab */ + -4, /* (299) cmd ::= create_vtab LP vtabarglist RP */ + -8, /* (300) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ + 0, /* (301) vtabarg ::= */ + -1, /* (302) vtabargtoken ::= ANY */ + -3, /* (303) vtabargtoken ::= lp anylist RP */ + -1, /* (304) lp ::= LP */ + -2, /* (305) with ::= WITH wqlist */ + -3, /* (306) with ::= WITH RECURSIVE wqlist */ + -1, /* (307) wqas ::= AS */ + -2, /* (308) wqas ::= AS MATERIALIZED */ + -3, /* (309) wqas ::= AS NOT MATERIALIZED */ + -6, /* (310) wqitem ::= withnm eidlist_opt wqas LP select RP */ + -1, /* (311) withnm ::= nm */ + -1, /* (312) wqlist ::= wqitem */ + -3, /* (313) wqlist ::= wqlist COMMA wqitem */ + -3, /* (314) windowdefn_list ::= windowdefn_list COMMA windowdefn */ + -5, /* (315) windowdefn ::= nm AS LP window RP */ + -5, /* (316) window ::= PARTITION BY nexprlist orderby_opt frame_opt */ + -6, /* (317) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ + -4, /* (318) window ::= ORDER BY sortlist frame_opt */ + -5, /* (319) window ::= nm ORDER BY sortlist frame_opt */ + -2, /* (320) window ::= nm frame_opt */ + 0, /* (321) frame_opt ::= */ + -3, /* (322) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ + -6, /* (323) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ + -1, /* (324) range_or_rows ::= RANGE|ROWS|GROUPS */ + -1, /* (325) frame_bound_s ::= frame_bound */ + -2, /* (326) frame_bound_s ::= UNBOUNDED PRECEDING */ + -1, /* (327) frame_bound_e ::= frame_bound */ + -2, /* (328) frame_bound_e ::= UNBOUNDED FOLLOWING */ + -2, /* (329) frame_bound ::= expr PRECEDING|FOLLOWING */ + -2, /* (330) frame_bound ::= CURRENT ROW */ + 0, /* (331) frame_exclude_opt ::= */ + -2, /* (332) frame_exclude_opt ::= EXCLUDE frame_exclude */ + -2, /* (333) frame_exclude ::= NO OTHERS */ + -2, /* (334) frame_exclude ::= CURRENT ROW */ + -1, /* (335) frame_exclude ::= GROUP|TIES */ + -2, /* (336) window_clause ::= WINDOW windowdefn_list */ + -2, /* (337) filter_over ::= filter_clause over_clause */ + -1, /* (338) filter_over ::= over_clause */ + -1, /* (339) filter_over ::= filter_clause */ + -4, /* (340) over_clause ::= OVER LP window RP */ + -2, /* (341) over_clause ::= OVER nm */ + -5, /* (342) filter_clause ::= FILTER LP WHERE expr RP */ + -1, /* (343) term ::= QNUMBER */ + -1, /* (344) input ::= cmdlist */ + -2, /* (345) cmdlist ::= cmdlist ecmd */ + -1, /* (346) cmdlist ::= ecmd */ + -1, /* (347) ecmd ::= SEMI */ + -2, /* (348) ecmd ::= cmdx SEMI */ + -3, /* (349) ecmd ::= explain cmdx SEMI */ + 0, /* (350) trans_opt ::= */ + -1, /* (351) trans_opt ::= TRANSACTION */ + -2, /* (352) trans_opt ::= TRANSACTION nm */ + -1, /* (353) savepoint_opt ::= SAVEPOINT */ + 0, /* (354) savepoint_opt ::= */ + -2, /* (355) cmd ::= create_table create_table_args */ + -1, /* (356) table_option_set ::= table_option */ + -4, /* (357) columnlist ::= columnlist COMMA columnname carglist */ + -2, /* (358) columnlist ::= columnname carglist */ + -1, /* (359) nm ::= ID|INDEXED|JOIN_KW */ + -1, /* (360) nm ::= STRING */ + -1, /* (361) typetoken ::= typename */ + -1, /* (362) typename ::= ID|STRING */ + -1, /* (363) signed ::= plus_num */ + -1, /* (364) signed ::= minus_num */ + -2, /* (365) carglist ::= carglist ccons */ + 0, /* (366) carglist ::= */ + -2, /* (367) ccons ::= NULL onconf */ + -4, /* (368) ccons ::= GENERATED ALWAYS AS generated */ + -2, /* (369) ccons ::= AS generated */ + -2, /* (370) conslist_opt ::= COMMA conslist */ + -3, /* (371) conslist ::= conslist tconscomma tcons */ + -1, /* (372) conslist ::= tcons */ + 0, /* (373) tconscomma ::= */ + -1, /* (374) defer_subclause_opt ::= defer_subclause */ + -1, /* (375) resolvetype ::= raisetype */ + -1, /* (376) selectnowith ::= oneselect */ + -1, /* (377) oneselect ::= values */ + -2, /* (378) sclp ::= selcollist COMMA */ + -1, /* (379) as ::= ID|STRING */ + -1, /* (380) indexed_opt ::= indexed_by */ + 0, /* (381) returning ::= */ + -1, /* (382) expr ::= term */ + -1, /* (383) likeop ::= LIKE_KW|MATCH */ + -1, /* (384) case_operand ::= expr */ + -1, /* (385) exprlist ::= nexprlist */ + -1, /* (386) nmnum ::= plus_num */ + -1, /* (387) nmnum ::= nm */ + -1, /* (388) nmnum ::= ON */ + -1, /* (389) nmnum ::= DELETE */ + -1, /* (390) nmnum ::= DEFAULT */ + -1, /* (391) plus_num ::= INTEGER|FLOAT */ + 0, /* (392) foreach_clause ::= */ + -3, /* (393) foreach_clause ::= FOR EACH ROW */ + -1, /* (394) trnm ::= nm */ + 0, /* (395) tridxby ::= */ + -1, /* (396) database_kw_opt ::= DATABASE */ + 0, /* (397) database_kw_opt ::= */ + 0, /* (398) kwcolumn_opt ::= */ + -1, /* (399) kwcolumn_opt ::= COLUMNKW */ + -1, /* (400) vtabarglist ::= vtabarg */ + -3, /* (401) vtabarglist ::= vtabarglist COMMA vtabarg */ + -2, /* (402) vtabarg ::= vtabarg vtabargtoken */ + 0, /* (403) anylist ::= */ + -4, /* (404) anylist ::= anylist LP anylist RP */ + -2, /* (405) anylist ::= anylist ANY */ + 0, /* (406) with ::= */ + -1, /* (407) windowdefn_list ::= windowdefn */ + -1, /* (408) window ::= frame_opt */ }; static void yy_accept(yyParser*); /* Forward Declaration */ @@ -174724,16 +176126,16 @@ static YYACTIONTYPE yy_reduce( { sqlite3FinishCoding(pParse); } break; case 3: /* cmd ::= BEGIN transtype trans_opt */ -{sqlite3BeginTransaction(pParse, yymsp[-1].minor.yy394);} +{sqlite3BeginTransaction(pParse, yymsp[-1].minor.yy144);} break; case 4: /* transtype ::= */ -{yymsp[1].minor.yy394 = TK_DEFERRED;} +{yymsp[1].minor.yy144 = TK_DEFERRED;} break; case 5: /* transtype ::= DEFERRED */ case 6: /* transtype ::= IMMEDIATE */ yytestcase(yyruleno==6); case 7: /* transtype ::= EXCLUSIVE */ yytestcase(yyruleno==7); - case 321: /* range_or_rows ::= RANGE|ROWS|GROUPS */ yytestcase(yyruleno==321); -{yymsp[0].minor.yy394 = yymsp[0].major; /*A-overwrites-X*/} + case 324: /* range_or_rows ::= RANGE|ROWS|GROUPS */ yytestcase(yyruleno==324); +{yymsp[0].minor.yy144 = yymsp[0].major; /*A-overwrites-X*/} break; case 8: /* cmd ::= COMMIT|END trans_opt */ case 9: /* cmd ::= ROLLBACK trans_opt */ yytestcase(yyruleno==9); @@ -174756,7 +176158,7 @@ static YYACTIONTYPE yy_reduce( break; case 13: /* create_table ::= createkw temp TABLE ifnotexists nm dbnm */ { - sqlite3StartTable(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,yymsp[-4].minor.yy394,0,0,yymsp[-2].minor.yy394); + sqlite3StartTable(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,yymsp[-4].minor.yy144,0,0,yymsp[-2].minor.yy144); } break; case 14: /* createkw ::= CREATE */ @@ -174768,40 +176170,40 @@ static YYACTIONTYPE yy_reduce( case 62: /* init_deferred_pred_opt ::= */ yytestcase(yyruleno==62); case 72: /* defer_subclause_opt ::= */ yytestcase(yyruleno==72); case 81: /* ifexists ::= */ yytestcase(yyruleno==81); - case 98: /* distinct ::= */ yytestcase(yyruleno==98); - case 244: /* collate ::= */ yytestcase(yyruleno==244); -{yymsp[1].minor.yy394 = 0;} + case 100: /* distinct ::= */ yytestcase(yyruleno==100); + case 246: /* collate ::= */ yytestcase(yyruleno==246); +{yymsp[1].minor.yy144 = 0;} break; case 16: /* ifnotexists ::= IF NOT EXISTS */ -{yymsp[-2].minor.yy394 = 1;} +{yymsp[-2].minor.yy144 = 1;} break; case 17: /* temp ::= TEMP */ -{yymsp[0].minor.yy394 = pParse->db->init.busy==0;} +{yymsp[0].minor.yy144 = pParse->db->init.busy==0;} break; case 19: /* create_table_args ::= LP columnlist conslist_opt RP table_option_set */ { - sqlite3EndTable(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,yymsp[0].minor.yy285,0); + sqlite3EndTable(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,yymsp[0].minor.yy391,0); } break; case 20: /* create_table_args ::= AS select */ { - sqlite3EndTable(pParse,0,0,0,yymsp[0].minor.yy47); - sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy47); + sqlite3EndTable(pParse,0,0,0,yymsp[0].minor.yy555); + sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy555); } break; case 21: /* table_option_set ::= */ -{yymsp[1].minor.yy285 = 0;} +{yymsp[1].minor.yy391 = 0;} break; case 22: /* table_option_set ::= table_option_set COMMA table_option */ -{yylhsminor.yy285 = yymsp[-2].minor.yy285|yymsp[0].minor.yy285;} - yymsp[-2].minor.yy285 = yylhsminor.yy285; +{yylhsminor.yy391 = yymsp[-2].minor.yy391|yymsp[0].minor.yy391;} + yymsp[-2].minor.yy391 = yylhsminor.yy391; break; case 23: /* table_option ::= WITHOUT nm */ { if( yymsp[0].minor.yy0.n==5 && sqlite3_strnicmp(yymsp[0].minor.yy0.z,"rowid",5)==0 ){ - yymsp[-1].minor.yy285 = TF_WithoutRowid | TF_NoVisibleRowid; + yymsp[-1].minor.yy391 = TF_WithoutRowid | TF_NoVisibleRowid; }else{ - yymsp[-1].minor.yy285 = 0; + yymsp[-1].minor.yy391 = 0; sqlite3ErrorMsg(pParse, "unknown table option: %.*s", yymsp[0].minor.yy0.n, yymsp[0].minor.yy0.z); } } @@ -174809,20 +176211,20 @@ static YYACTIONTYPE yy_reduce( case 24: /* table_option ::= nm */ { if( yymsp[0].minor.yy0.n==6 && sqlite3_strnicmp(yymsp[0].minor.yy0.z,"strict",6)==0 ){ - yylhsminor.yy285 = TF_Strict; + yylhsminor.yy391 = TF_Strict; }else{ - yylhsminor.yy285 = 0; + yylhsminor.yy391 = 0; sqlite3ErrorMsg(pParse, "unknown table option: %.*s", yymsp[0].minor.yy0.n, yymsp[0].minor.yy0.z); } } - yymsp[0].minor.yy285 = yylhsminor.yy285; + yymsp[0].minor.yy391 = yylhsminor.yy391; break; case 25: /* columnname ::= nm typetoken */ {sqlite3AddColumn(pParse,yymsp[-1].minor.yy0,yymsp[0].minor.yy0);} break; case 26: /* typetoken ::= */ case 65: /* conslist_opt ::= */ yytestcase(yyruleno==65); - case 104: /* as ::= */ yytestcase(yyruleno==104); + case 106: /* as ::= */ yytestcase(yyruleno==106); {yymsp[1].minor.yy0.n = 0; yymsp[1].minor.yy0.z = 0;} break; case 27: /* typetoken ::= typename LP signed RP */ @@ -174841,7 +176243,7 @@ static YYACTIONTYPE yy_reduce( case 30: /* scanpt ::= */ { assert( yyLookahead!=YYNOCODE ); - yymsp[1].minor.yy522 = yyLookaheadToken.z; + yymsp[1].minor.yy168 = yyLookaheadToken.z; } break; case 31: /* scantok ::= */ @@ -174855,17 +176257,17 @@ static YYACTIONTYPE yy_reduce( {pParse->constraintName = yymsp[0].minor.yy0;} break; case 33: /* ccons ::= DEFAULT scantok term */ -{sqlite3AddDefaultValue(pParse,yymsp[0].minor.yy528,yymsp[-1].minor.yy0.z,&yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]);} +{sqlite3AddDefaultValue(pParse,yymsp[0].minor.yy454,yymsp[-1].minor.yy0.z,&yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]);} break; case 34: /* ccons ::= DEFAULT LP expr RP */ -{sqlite3AddDefaultValue(pParse,yymsp[-1].minor.yy528,yymsp[-2].minor.yy0.z+1,yymsp[0].minor.yy0.z);} +{sqlite3AddDefaultValue(pParse,yymsp[-1].minor.yy454,yymsp[-2].minor.yy0.z+1,yymsp[0].minor.yy0.z);} break; case 35: /* ccons ::= DEFAULT PLUS scantok term */ -{sqlite3AddDefaultValue(pParse,yymsp[0].minor.yy528,yymsp[-2].minor.yy0.z,&yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]);} +{sqlite3AddDefaultValue(pParse,yymsp[0].minor.yy454,yymsp[-2].minor.yy0.z,&yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]);} break; case 36: /* ccons ::= DEFAULT MINUS scantok term */ { - Expr *p = sqlite3PExpr(pParse, TK_UMINUS, yymsp[0].minor.yy528, 0); + Expr *p = sqlite3PExpr(pParse, TK_UMINUS, yymsp[0].minor.yy454, 0); sqlite3AddDefaultValue(pParse,p,yymsp[-2].minor.yy0.z,&yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]); } break; @@ -174880,151 +176282,151 @@ static YYACTIONTYPE yy_reduce( } break; case 38: /* ccons ::= NOT NULL onconf */ -{sqlite3AddNotNull(pParse, yymsp[0].minor.yy394);} +{sqlite3AddNotNull(pParse, yymsp[0].minor.yy144);} break; case 39: /* ccons ::= PRIMARY KEY sortorder onconf autoinc */ -{sqlite3AddPrimaryKey(pParse,0,yymsp[-1].minor.yy394,yymsp[0].minor.yy394,yymsp[-2].minor.yy394);} +{sqlite3AddPrimaryKey(pParse,0,yymsp[-1].minor.yy144,yymsp[0].minor.yy144,yymsp[-2].minor.yy144);} break; case 40: /* ccons ::= UNIQUE onconf */ -{sqlite3CreateIndex(pParse,0,0,0,0,yymsp[0].minor.yy394,0,0,0,0, +{sqlite3CreateIndex(pParse,0,0,0,0,yymsp[0].minor.yy144,0,0,0,0, SQLITE_IDXTYPE_UNIQUE);} break; case 41: /* ccons ::= CHECK LP expr RP */ -{sqlite3AddCheckConstraint(pParse,yymsp[-1].minor.yy528,yymsp[-2].minor.yy0.z,yymsp[0].minor.yy0.z);} +{sqlite3AddCheckConstraint(pParse,yymsp[-1].minor.yy454,yymsp[-2].minor.yy0.z,yymsp[0].minor.yy0.z);} break; case 42: /* ccons ::= REFERENCES nm eidlist_opt refargs */ -{sqlite3CreateForeignKey(pParse,0,&yymsp[-2].minor.yy0,yymsp[-1].minor.yy322,yymsp[0].minor.yy394);} +{sqlite3CreateForeignKey(pParse,0,&yymsp[-2].minor.yy0,yymsp[-1].minor.yy14,yymsp[0].minor.yy144);} break; case 43: /* ccons ::= defer_subclause */ -{sqlite3DeferForeignKey(pParse,yymsp[0].minor.yy394);} +{sqlite3DeferForeignKey(pParse,yymsp[0].minor.yy144);} break; case 44: /* ccons ::= COLLATE ID|STRING */ {sqlite3AddCollateType(pParse, &yymsp[0].minor.yy0);} break; case 45: /* generated ::= LP expr RP */ -{sqlite3AddGenerated(pParse,yymsp[-1].minor.yy528,0);} +{sqlite3AddGenerated(pParse,yymsp[-1].minor.yy454,0);} break; case 46: /* generated ::= LP expr RP ID */ -{sqlite3AddGenerated(pParse,yymsp[-2].minor.yy528,&yymsp[0].minor.yy0);} +{sqlite3AddGenerated(pParse,yymsp[-2].minor.yy454,&yymsp[0].minor.yy0);} break; case 48: /* autoinc ::= AUTOINCR */ -{yymsp[0].minor.yy394 = 1;} +{yymsp[0].minor.yy144 = 1;} break; case 49: /* refargs ::= */ -{ yymsp[1].minor.yy394 = OE_None*0x0101; /* EV: R-19803-45884 */} +{ yymsp[1].minor.yy144 = OE_None*0x0101; /* EV: R-19803-45884 */} break; case 50: /* refargs ::= refargs refarg */ -{ yymsp[-1].minor.yy394 = (yymsp[-1].minor.yy394 & ~yymsp[0].minor.yy231.mask) | yymsp[0].minor.yy231.value; } +{ yymsp[-1].minor.yy144 = (yymsp[-1].minor.yy144 & ~yymsp[0].minor.yy383.mask) | yymsp[0].minor.yy383.value; } break; case 51: /* refarg ::= MATCH nm */ -{ yymsp[-1].minor.yy231.value = 0; yymsp[-1].minor.yy231.mask = 0x000000; } +{ yymsp[-1].minor.yy383.value = 0; yymsp[-1].minor.yy383.mask = 0x000000; } break; case 52: /* refarg ::= ON INSERT refact */ -{ yymsp[-2].minor.yy231.value = 0; yymsp[-2].minor.yy231.mask = 0x000000; } +{ yymsp[-2].minor.yy383.value = 0; yymsp[-2].minor.yy383.mask = 0x000000; } break; case 53: /* refarg ::= ON DELETE refact */ -{ yymsp[-2].minor.yy231.value = yymsp[0].minor.yy394; yymsp[-2].minor.yy231.mask = 0x0000ff; } +{ yymsp[-2].minor.yy383.value = yymsp[0].minor.yy144; yymsp[-2].minor.yy383.mask = 0x0000ff; } break; case 54: /* refarg ::= ON UPDATE refact */ -{ yymsp[-2].minor.yy231.value = yymsp[0].minor.yy394<<8; yymsp[-2].minor.yy231.mask = 0x00ff00; } +{ yymsp[-2].minor.yy383.value = yymsp[0].minor.yy144<<8; yymsp[-2].minor.yy383.mask = 0x00ff00; } break; case 55: /* refact ::= SET NULL */ -{ yymsp[-1].minor.yy394 = OE_SetNull; /* EV: R-33326-45252 */} +{ yymsp[-1].minor.yy144 = OE_SetNull; /* EV: R-33326-45252 */} break; case 56: /* refact ::= SET DEFAULT */ -{ yymsp[-1].minor.yy394 = OE_SetDflt; /* EV: R-33326-45252 */} +{ yymsp[-1].minor.yy144 = OE_SetDflt; /* EV: R-33326-45252 */} break; case 57: /* refact ::= CASCADE */ -{ yymsp[0].minor.yy394 = OE_Cascade; /* EV: R-33326-45252 */} +{ yymsp[0].minor.yy144 = OE_Cascade; /* EV: R-33326-45252 */} break; case 58: /* refact ::= RESTRICT */ -{ yymsp[0].minor.yy394 = OE_Restrict; /* EV: R-33326-45252 */} +{ yymsp[0].minor.yy144 = OE_Restrict; /* EV: R-33326-45252 */} break; case 59: /* refact ::= NO ACTION */ -{ yymsp[-1].minor.yy394 = OE_None; /* EV: R-33326-45252 */} +{ yymsp[-1].minor.yy144 = OE_None; /* EV: R-33326-45252 */} break; case 60: /* defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */ -{yymsp[-2].minor.yy394 = 0;} +{yymsp[-2].minor.yy144 = 0;} break; case 61: /* defer_subclause ::= DEFERRABLE init_deferred_pred_opt */ case 76: /* orconf ::= OR resolvetype */ yytestcase(yyruleno==76); - case 171: /* insert_cmd ::= INSERT orconf */ yytestcase(yyruleno==171); -{yymsp[-1].minor.yy394 = yymsp[0].minor.yy394;} + case 173: /* insert_cmd ::= INSERT orconf */ yytestcase(yyruleno==173); +{yymsp[-1].minor.yy144 = yymsp[0].minor.yy144;} break; case 63: /* init_deferred_pred_opt ::= INITIALLY DEFERRED */ case 80: /* ifexists ::= IF EXISTS */ yytestcase(yyruleno==80); - 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;} + case 219: /* between_op ::= NOT BETWEEN */ yytestcase(yyruleno==219); + case 222: /* in_op ::= NOT IN */ yytestcase(yyruleno==222); + case 247: /* collate ::= COLLATE ID|STRING */ yytestcase(yyruleno==247); +{yymsp[-1].minor.yy144 = 1;} break; case 64: /* init_deferred_pred_opt ::= INITIALLY IMMEDIATE */ -{yymsp[-1].minor.yy394 = 0;} +{yymsp[-1].minor.yy144 = 0;} break; case 66: /* tconscomma ::= COMMA */ {pParse->constraintName.n = 0;} break; case 68: /* tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */ -{sqlite3AddPrimaryKey(pParse,yymsp[-3].minor.yy322,yymsp[0].minor.yy394,yymsp[-2].minor.yy394,0);} +{sqlite3AddPrimaryKey(pParse,yymsp[-3].minor.yy14,yymsp[0].minor.yy144,yymsp[-2].minor.yy144,0);} break; case 69: /* tcons ::= UNIQUE LP sortlist RP onconf */ -{sqlite3CreateIndex(pParse,0,0,0,yymsp[-2].minor.yy322,yymsp[0].minor.yy394,0,0,0,0, +{sqlite3CreateIndex(pParse,0,0,0,yymsp[-2].minor.yy14,yymsp[0].minor.yy144,0,0,0,0, SQLITE_IDXTYPE_UNIQUE);} break; case 70: /* tcons ::= CHECK LP expr RP onconf */ -{sqlite3AddCheckConstraint(pParse,yymsp[-2].minor.yy528,yymsp[-3].minor.yy0.z,yymsp[-1].minor.yy0.z);} +{sqlite3AddCheckConstraint(pParse,yymsp[-2].minor.yy454,yymsp[-3].minor.yy0.z,yymsp[-1].minor.yy0.z);} break; case 71: /* tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */ { - sqlite3CreateForeignKey(pParse, yymsp[-6].minor.yy322, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy322, yymsp[-1].minor.yy394); - sqlite3DeferForeignKey(pParse, yymsp[0].minor.yy394); + sqlite3CreateForeignKey(pParse, yymsp[-6].minor.yy14, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy14, yymsp[-1].minor.yy144); + sqlite3DeferForeignKey(pParse, yymsp[0].minor.yy144); } break; case 73: /* onconf ::= */ case 75: /* orconf ::= */ yytestcase(yyruleno==75); -{yymsp[1].minor.yy394 = OE_Default;} +{yymsp[1].minor.yy144 = OE_Default;} break; case 74: /* onconf ::= ON CONFLICT resolvetype */ -{yymsp[-2].minor.yy394 = yymsp[0].minor.yy394;} +{yymsp[-2].minor.yy144 = yymsp[0].minor.yy144;} break; case 77: /* resolvetype ::= IGNORE */ -{yymsp[0].minor.yy394 = OE_Ignore;} +{yymsp[0].minor.yy144 = OE_Ignore;} break; case 78: /* resolvetype ::= REPLACE */ - case 172: /* insert_cmd ::= REPLACE */ yytestcase(yyruleno==172); -{yymsp[0].minor.yy394 = OE_Replace;} + case 174: /* insert_cmd ::= REPLACE */ yytestcase(yyruleno==174); +{yymsp[0].minor.yy144 = OE_Replace;} break; case 79: /* cmd ::= DROP TABLE ifexists fullname */ { - sqlite3DropTable(pParse, yymsp[0].minor.yy131, 0, yymsp[-1].minor.yy394); + sqlite3DropTable(pParse, yymsp[0].minor.yy203, 0, yymsp[-1].minor.yy144); } break; case 82: /* cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */ { - sqlite3CreateView(pParse, &yymsp[-8].minor.yy0, &yymsp[-4].minor.yy0, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy322, yymsp[0].minor.yy47, yymsp[-7].minor.yy394, yymsp[-5].minor.yy394); + sqlite3CreateView(pParse, &yymsp[-8].minor.yy0, &yymsp[-4].minor.yy0, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy14, yymsp[0].minor.yy555, yymsp[-7].minor.yy144, yymsp[-5].minor.yy144); } break; case 83: /* cmd ::= DROP VIEW ifexists fullname */ { - sqlite3DropTable(pParse, yymsp[0].minor.yy131, 1, yymsp[-1].minor.yy394); + sqlite3DropTable(pParse, yymsp[0].minor.yy203, 1, yymsp[-1].minor.yy144); } break; case 84: /* cmd ::= select */ { SelectDest dest = {SRT_Output, 0, 0, 0, 0, 0, 0}; - sqlite3Select(pParse, yymsp[0].minor.yy47, &dest); - sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy47); + sqlite3Select(pParse, yymsp[0].minor.yy555, &dest); + sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy555); } break; case 85: /* select ::= WITH wqlist selectnowith */ -{yymsp[-2].minor.yy47 = attachWithToSelect(pParse,yymsp[0].minor.yy47,yymsp[-1].minor.yy521);} +{yymsp[-2].minor.yy555 = attachWithToSelect(pParse,yymsp[0].minor.yy555,yymsp[-1].minor.yy59);} break; case 86: /* select ::= WITH RECURSIVE wqlist selectnowith */ -{yymsp[-3].minor.yy47 = attachWithToSelect(pParse,yymsp[0].minor.yy47,yymsp[-1].minor.yy521);} +{yymsp[-3].minor.yy555 = attachWithToSelect(pParse,yymsp[0].minor.yy555,yymsp[-1].minor.yy59);} break; case 87: /* select ::= selectnowith */ { - Select *p = yymsp[0].minor.yy47; + Select *p = yymsp[0].minor.yy555; if( p ){ parserDoubleLinkSelect(pParse, p); } @@ -175032,8 +176434,8 @@ static YYACTIONTYPE yy_reduce( break; case 88: /* selectnowith ::= selectnowith multiselect_op oneselect */ { - Select *pRhs = yymsp[0].minor.yy47; - Select *pLhs = yymsp[-2].minor.yy47; + Select *pRhs = yymsp[0].minor.yy555; + Select *pLhs = yymsp[-2].minor.yy555; if( pRhs && pRhs->pPrior ){ SrcList *pFrom; Token x; @@ -175043,148 +176445,145 @@ static YYACTIONTYPE yy_reduce( pRhs = sqlite3SelectNew(pParse,0,pFrom,0,0,0,0,0,0); } if( pRhs ){ - pRhs->op = (u8)yymsp[-1].minor.yy394; + pRhs->op = (u8)yymsp[-1].minor.yy144; pRhs->pPrior = pLhs; if( ALWAYS(pLhs) ) pLhs->selFlags &= ~SF_MultiValue; pRhs->selFlags &= ~SF_MultiValue; - if( yymsp[-1].minor.yy394!=TK_ALL ) pParse->hasCompound = 1; + if( yymsp[-1].minor.yy144!=TK_ALL ) pParse->hasCompound = 1; }else{ sqlite3SelectDelete(pParse->db, pLhs); } - yymsp[-2].minor.yy47 = pRhs; + yymsp[-2].minor.yy555 = pRhs; } break; case 89: /* multiselect_op ::= UNION */ case 91: /* multiselect_op ::= EXCEPT|INTERSECT */ yytestcase(yyruleno==91); -{yymsp[0].minor.yy394 = yymsp[0].major; /*A-overwrites-OP*/} +{yymsp[0].minor.yy144 = yymsp[0].major; /*A-overwrites-OP*/} break; case 90: /* multiselect_op ::= UNION ALL */ -{yymsp[-1].minor.yy394 = TK_ALL;} +{yymsp[-1].minor.yy144 = TK_ALL;} break; case 92: /* oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */ { - yymsp[-8].minor.yy47 = sqlite3SelectNew(pParse,yymsp[-6].minor.yy322,yymsp[-5].minor.yy131,yymsp[-4].minor.yy528,yymsp[-3].minor.yy322,yymsp[-2].minor.yy528,yymsp[-1].minor.yy322,yymsp[-7].minor.yy394,yymsp[0].minor.yy528); + yymsp[-8].minor.yy555 = sqlite3SelectNew(pParse,yymsp[-6].minor.yy14,yymsp[-5].minor.yy203,yymsp[-4].minor.yy454,yymsp[-3].minor.yy14,yymsp[-2].minor.yy454,yymsp[-1].minor.yy14,yymsp[-7].minor.yy144,yymsp[0].minor.yy454); } break; case 93: /* oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */ { - yymsp[-9].minor.yy47 = sqlite3SelectNew(pParse,yymsp[-7].minor.yy322,yymsp[-6].minor.yy131,yymsp[-5].minor.yy528,yymsp[-4].minor.yy322,yymsp[-3].minor.yy528,yymsp[-1].minor.yy322,yymsp[-8].minor.yy394,yymsp[0].minor.yy528); - if( yymsp[-9].minor.yy47 ){ - yymsp[-9].minor.yy47->pWinDefn = yymsp[-2].minor.yy41; + yymsp[-9].minor.yy555 = sqlite3SelectNew(pParse,yymsp[-7].minor.yy14,yymsp[-6].minor.yy203,yymsp[-5].minor.yy454,yymsp[-4].minor.yy14,yymsp[-3].minor.yy454,yymsp[-1].minor.yy14,yymsp[-8].minor.yy144,yymsp[0].minor.yy454); + if( yymsp[-9].minor.yy555 ){ + yymsp[-9].minor.yy555->pWinDefn = yymsp[-2].minor.yy211; }else{ - sqlite3WindowListDelete(pParse->db, yymsp[-2].minor.yy41); + sqlite3WindowListDelete(pParse->db, yymsp[-2].minor.yy211); } } break; case 94: /* values ::= VALUES LP nexprlist RP */ { - yymsp[-3].minor.yy47 = sqlite3SelectNew(pParse,yymsp[-1].minor.yy322,0,0,0,0,0,SF_Values,0); + yymsp[-3].minor.yy555 = sqlite3SelectNew(pParse,yymsp[-1].minor.yy14,0,0,0,0,0,SF_Values,0); } break; - case 95: /* values ::= values COMMA LP nexprlist RP */ + case 95: /* oneselect ::= mvalues */ { - Select *pRight, *pLeft = yymsp[-4].minor.yy47; - pRight = sqlite3SelectNew(pParse,yymsp[-1].minor.yy322,0,0,0,0,0,SF_Values|SF_MultiValue,0); - if( ALWAYS(pLeft) ) pLeft->selFlags &= ~SF_MultiValue; - if( pRight ){ - pRight->op = TK_ALL; - pRight->pPrior = pLeft; - yymsp[-4].minor.yy47 = pRight; - }else{ - yymsp[-4].minor.yy47 = pLeft; - } + sqlite3MultiValuesEnd(pParse, yymsp[0].minor.yy555); } break; - case 96: /* distinct ::= DISTINCT */ -{yymsp[0].minor.yy394 = SF_Distinct;} - break; - case 97: /* distinct ::= ALL */ -{yymsp[0].minor.yy394 = SF_All;} - break; - case 99: /* sclp ::= */ - case 132: /* orderby_opt ::= */ yytestcase(yyruleno==132); - case 142: /* groupby_opt ::= */ yytestcase(yyruleno==142); - 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 */ + case 96: /* mvalues ::= values COMMA LP nexprlist RP */ + case 97: /* mvalues ::= mvalues COMMA LP nexprlist RP */ yytestcase(yyruleno==97); { - yymsp[-4].minor.yy322 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy322, yymsp[-2].minor.yy528); - if( yymsp[0].minor.yy0.n>0 ) sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy322, &yymsp[0].minor.yy0, 1); - sqlite3ExprListSetSpan(pParse,yymsp[-4].minor.yy322,yymsp[-3].minor.yy522,yymsp[-1].minor.yy522); + yymsp[-4].minor.yy555 = sqlite3MultiValues(pParse, yymsp[-4].minor.yy555, yymsp[-1].minor.yy14); } break; - case 101: /* selcollist ::= sclp scanpt STAR */ + case 98: /* distinct ::= DISTINCT */ +{yymsp[0].minor.yy144 = SF_Distinct;} + break; + case 99: /* distinct ::= ALL */ +{yymsp[0].minor.yy144 = SF_All;} + break; + case 101: /* sclp ::= */ + case 134: /* orderby_opt ::= */ yytestcase(yyruleno==134); + case 144: /* groupby_opt ::= */ yytestcase(yyruleno==144); + case 234: /* exprlist ::= */ yytestcase(yyruleno==234); + case 237: /* paren_exprlist ::= */ yytestcase(yyruleno==237); + case 242: /* eidlist_opt ::= */ yytestcase(yyruleno==242); +{yymsp[1].minor.yy14 = 0;} + break; + case 102: /* selcollist ::= sclp scanpt expr scanpt as */ +{ + yymsp[-4].minor.yy14 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy14, yymsp[-2].minor.yy454); + if( yymsp[0].minor.yy0.n>0 ) sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy14, &yymsp[0].minor.yy0, 1); + sqlite3ExprListSetSpan(pParse,yymsp[-4].minor.yy14,yymsp[-3].minor.yy168,yymsp[-1].minor.yy168); +} + break; + case 103: /* selcollist ::= sclp scanpt STAR */ { Expr *p = sqlite3Expr(pParse->db, TK_ASTERISK, 0); sqlite3ExprSetErrorOffset(p, (int)(yymsp[0].minor.yy0.z - pParse->zTail)); - yymsp[-2].minor.yy322 = sqlite3ExprListAppend(pParse, yymsp[-2].minor.yy322, p); + yymsp[-2].minor.yy14 = sqlite3ExprListAppend(pParse, yymsp[-2].minor.yy14, p); } break; - case 102: /* selcollist ::= sclp scanpt nm DOT STAR */ + case 104: /* selcollist ::= sclp scanpt nm DOT STAR */ { Expr *pRight, *pLeft, *pDot; pRight = sqlite3PExpr(pParse, TK_ASTERISK, 0, 0); sqlite3ExprSetErrorOffset(pRight, (int)(yymsp[0].minor.yy0.z - pParse->zTail)); pLeft = tokenExpr(pParse, TK_ID, yymsp[-2].minor.yy0); pDot = sqlite3PExpr(pParse, TK_DOT, pLeft, pRight); - yymsp[-4].minor.yy322 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy322, pDot); + yymsp[-4].minor.yy14 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy14, pDot); } break; - case 103: /* as ::= AS nm */ - case 115: /* dbnm ::= DOT nm */ yytestcase(yyruleno==115); - case 256: /* plus_num ::= PLUS INTEGER|FLOAT */ yytestcase(yyruleno==256); - case 257: /* minus_num ::= MINUS INTEGER|FLOAT */ yytestcase(yyruleno==257); + case 105: /* as ::= AS nm */ + case 117: /* dbnm ::= DOT nm */ yytestcase(yyruleno==117); + case 258: /* plus_num ::= PLUS INTEGER|FLOAT */ yytestcase(yyruleno==258); + case 259: /* minus_num ::= MINUS INTEGER|FLOAT */ yytestcase(yyruleno==259); {yymsp[-1].minor.yy0 = yymsp[0].minor.yy0;} break; - case 105: /* from ::= */ - case 108: /* stl_prefix ::= */ yytestcase(yyruleno==108); -{yymsp[1].minor.yy131 = 0;} + case 107: /* from ::= */ + case 110: /* stl_prefix ::= */ yytestcase(yyruleno==110); +{yymsp[1].minor.yy203 = 0;} break; - case 106: /* from ::= FROM seltablist */ + case 108: /* from ::= FROM seltablist */ { - yymsp[-1].minor.yy131 = yymsp[0].minor.yy131; - sqlite3SrcListShiftJoinType(pParse,yymsp[-1].minor.yy131); + yymsp[-1].minor.yy203 = yymsp[0].minor.yy203; + sqlite3SrcListShiftJoinType(pParse,yymsp[-1].minor.yy203); } break; - case 107: /* stl_prefix ::= seltablist joinop */ + case 109: /* stl_prefix ::= seltablist joinop */ { - if( ALWAYS(yymsp[-1].minor.yy131 && yymsp[-1].minor.yy131->nSrc>0) ) yymsp[-1].minor.yy131->a[yymsp[-1].minor.yy131->nSrc-1].fg.jointype = (u8)yymsp[0].minor.yy394; + if( ALWAYS(yymsp[-1].minor.yy203 && yymsp[-1].minor.yy203->nSrc>0) ) yymsp[-1].minor.yy203->a[yymsp[-1].minor.yy203->nSrc-1].fg.jointype = (u8)yymsp[0].minor.yy144; } break; - case 109: /* seltablist ::= stl_prefix nm dbnm as on_using */ + case 111: /* seltablist ::= stl_prefix nm dbnm as on_using */ { - yymsp[-4].minor.yy131 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-4].minor.yy131,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,0,&yymsp[0].minor.yy561); + yymsp[-4].minor.yy203 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-4].minor.yy203,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,0,&yymsp[0].minor.yy269); } break; - case 110: /* seltablist ::= stl_prefix nm dbnm as indexed_by on_using */ + case 112: /* seltablist ::= stl_prefix nm dbnm as indexed_by on_using */ { - yymsp[-5].minor.yy131 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy131,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,0,&yymsp[0].minor.yy561); - sqlite3SrcListIndexedBy(pParse, yymsp[-5].minor.yy131, &yymsp[-1].minor.yy0); + yymsp[-5].minor.yy203 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy203,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,0,&yymsp[0].minor.yy269); + sqlite3SrcListIndexedBy(pParse, yymsp[-5].minor.yy203, &yymsp[-1].minor.yy0); } break; - case 111: /* seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_using */ + case 113: /* seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_using */ { - yymsp[-7].minor.yy131 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-7].minor.yy131,&yymsp[-6].minor.yy0,&yymsp[-5].minor.yy0,&yymsp[-1].minor.yy0,0,&yymsp[0].minor.yy561); - sqlite3SrcListFuncArgs(pParse, yymsp[-7].minor.yy131, yymsp[-3].minor.yy322); + yymsp[-7].minor.yy203 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-7].minor.yy203,&yymsp[-6].minor.yy0,&yymsp[-5].minor.yy0,&yymsp[-1].minor.yy0,0,&yymsp[0].minor.yy269); + sqlite3SrcListFuncArgs(pParse, yymsp[-7].minor.yy203, yymsp[-3].minor.yy14); } break; - case 112: /* seltablist ::= stl_prefix LP select RP as on_using */ + case 114: /* seltablist ::= stl_prefix LP select RP as on_using */ { - yymsp[-5].minor.yy131 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy131,0,0,&yymsp[-1].minor.yy0,yymsp[-3].minor.yy47,&yymsp[0].minor.yy561); + yymsp[-5].minor.yy203 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy203,0,0,&yymsp[-1].minor.yy0,yymsp[-3].minor.yy555,&yymsp[0].minor.yy269); } break; - case 113: /* seltablist ::= stl_prefix LP seltablist RP as on_using */ + case 115: /* seltablist ::= stl_prefix LP seltablist RP as on_using */ { - if( yymsp[-5].minor.yy131==0 && yymsp[-1].minor.yy0.n==0 && yymsp[0].minor.yy561.pOn==0 && yymsp[0].minor.yy561.pUsing==0 ){ - yymsp[-5].minor.yy131 = yymsp[-3].minor.yy131; - }else if( ALWAYS(yymsp[-3].minor.yy131!=0) && yymsp[-3].minor.yy131->nSrc==1 ){ - yymsp[-5].minor.yy131 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy131,0,0,&yymsp[-1].minor.yy0,0,&yymsp[0].minor.yy561); - if( yymsp[-5].minor.yy131 ){ - SrcItem *pNew = &yymsp[-5].minor.yy131->a[yymsp[-5].minor.yy131->nSrc-1]; - SrcItem *pOld = yymsp[-3].minor.yy131->a; + if( yymsp[-5].minor.yy203==0 && yymsp[-1].minor.yy0.n==0 && yymsp[0].minor.yy269.pOn==0 && yymsp[0].minor.yy269.pUsing==0 ){ + yymsp[-5].minor.yy203 = yymsp[-3].minor.yy203; + }else if( ALWAYS(yymsp[-3].minor.yy203!=0) && yymsp[-3].minor.yy203->nSrc==1 ){ + yymsp[-5].minor.yy203 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy203,0,0,&yymsp[-1].minor.yy0,0,&yymsp[0].minor.yy269); + if( yymsp[-5].minor.yy203 ){ + SrcItem *pNew = &yymsp[-5].minor.yy203->a[yymsp[-5].minor.yy203->nSrc-1]; + SrcItem *pOld = yymsp[-3].minor.yy203->a; pNew->zName = pOld->zName; pNew->zDatabase = pOld->zDatabase; pNew->pSelect = pOld->pSelect; @@ -175200,153 +176599,153 @@ static YYACTIONTYPE yy_reduce( pOld->zName = pOld->zDatabase = 0; pOld->pSelect = 0; } - sqlite3SrcListDelete(pParse->db, yymsp[-3].minor.yy131); + sqlite3SrcListDelete(pParse->db, yymsp[-3].minor.yy203); }else{ Select *pSubquery; - sqlite3SrcListShiftJoinType(pParse,yymsp[-3].minor.yy131); - pSubquery = sqlite3SelectNew(pParse,0,yymsp[-3].minor.yy131,0,0,0,0,SF_NestedFrom,0); - yymsp[-5].minor.yy131 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy131,0,0,&yymsp[-1].minor.yy0,pSubquery,&yymsp[0].minor.yy561); + sqlite3SrcListShiftJoinType(pParse,yymsp[-3].minor.yy203); + pSubquery = sqlite3SelectNew(pParse,0,yymsp[-3].minor.yy203,0,0,0,0,SF_NestedFrom,0); + yymsp[-5].minor.yy203 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy203,0,0,&yymsp[-1].minor.yy0,pSubquery,&yymsp[0].minor.yy269); } } break; - case 114: /* dbnm ::= */ - case 129: /* indexed_opt ::= */ yytestcase(yyruleno==129); + case 116: /* dbnm ::= */ + case 131: /* indexed_opt ::= */ yytestcase(yyruleno==131); {yymsp[1].minor.yy0.z=0; yymsp[1].minor.yy0.n=0;} break; - case 116: /* fullname ::= nm */ + case 118: /* fullname ::= nm */ { - yylhsminor.yy131 = sqlite3SrcListAppend(pParse,0,&yymsp[0].minor.yy0,0); - if( IN_RENAME_OBJECT && yylhsminor.yy131 ) sqlite3RenameTokenMap(pParse, yylhsminor.yy131->a[0].zName, &yymsp[0].minor.yy0); + yylhsminor.yy203 = sqlite3SrcListAppend(pParse,0,&yymsp[0].minor.yy0,0); + if( IN_RENAME_OBJECT && yylhsminor.yy203 ) sqlite3RenameTokenMap(pParse, yylhsminor.yy203->a[0].zName, &yymsp[0].minor.yy0); } - yymsp[0].minor.yy131 = yylhsminor.yy131; + yymsp[0].minor.yy203 = yylhsminor.yy203; break; - case 117: /* fullname ::= nm DOT nm */ + case 119: /* fullname ::= nm DOT nm */ { - yylhsminor.yy131 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); - if( IN_RENAME_OBJECT && yylhsminor.yy131 ) sqlite3RenameTokenMap(pParse, yylhsminor.yy131->a[0].zName, &yymsp[0].minor.yy0); + yylhsminor.yy203 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); + if( IN_RENAME_OBJECT && yylhsminor.yy203 ) sqlite3RenameTokenMap(pParse, yylhsminor.yy203->a[0].zName, &yymsp[0].minor.yy0); } - yymsp[-2].minor.yy131 = yylhsminor.yy131; + yymsp[-2].minor.yy203 = yylhsminor.yy203; break; - case 118: /* xfullname ::= nm */ -{yymsp[0].minor.yy131 = sqlite3SrcListAppend(pParse,0,&yymsp[0].minor.yy0,0); /*A-overwrites-X*/} + case 120: /* xfullname ::= nm */ +{yymsp[0].minor.yy203 = sqlite3SrcListAppend(pParse,0,&yymsp[0].minor.yy0,0); /*A-overwrites-X*/} break; - case 119: /* xfullname ::= nm DOT nm */ -{yymsp[-2].minor.yy131 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-X*/} + case 121: /* xfullname ::= nm DOT nm */ +{yymsp[-2].minor.yy203 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-X*/} break; - case 120: /* xfullname ::= nm DOT nm AS nm */ + case 122: /* xfullname ::= nm DOT nm AS nm */ { - yymsp[-4].minor.yy131 = sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,&yymsp[-2].minor.yy0); /*A-overwrites-X*/ - if( yymsp[-4].minor.yy131 ) yymsp[-4].minor.yy131->a[0].zAlias = sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0); + yymsp[-4].minor.yy203 = sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,&yymsp[-2].minor.yy0); /*A-overwrites-X*/ + if( yymsp[-4].minor.yy203 ) yymsp[-4].minor.yy203->a[0].zAlias = sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0); } break; - case 121: /* xfullname ::= nm AS nm */ + case 123: /* xfullname ::= nm AS nm */ { - yymsp[-2].minor.yy131 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,0); /*A-overwrites-X*/ - if( yymsp[-2].minor.yy131 ) yymsp[-2].minor.yy131->a[0].zAlias = sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0); + yymsp[-2].minor.yy203 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,0); /*A-overwrites-X*/ + if( yymsp[-2].minor.yy203 ) yymsp[-2].minor.yy203->a[0].zAlias = sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0); } break; - case 122: /* joinop ::= COMMA|JOIN */ -{ yymsp[0].minor.yy394 = JT_INNER; } + case 124: /* joinop ::= COMMA|JOIN */ +{ yymsp[0].minor.yy144 = JT_INNER; } break; - case 123: /* joinop ::= JOIN_KW JOIN */ -{yymsp[-1].minor.yy394 = sqlite3JoinType(pParse,&yymsp[-1].minor.yy0,0,0); /*X-overwrites-A*/} + case 125: /* joinop ::= JOIN_KW JOIN */ +{yymsp[-1].minor.yy144 = sqlite3JoinType(pParse,&yymsp[-1].minor.yy0,0,0); /*X-overwrites-A*/} break; - case 124: /* joinop ::= JOIN_KW nm JOIN */ -{yymsp[-2].minor.yy394 = sqlite3JoinType(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,0); /*X-overwrites-A*/} + case 126: /* joinop ::= JOIN_KW nm JOIN */ +{yymsp[-2].minor.yy144 = sqlite3JoinType(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,0); /*X-overwrites-A*/} break; - case 125: /* joinop ::= JOIN_KW nm nm JOIN */ -{yymsp[-3].minor.yy394 = sqlite3JoinType(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0);/*X-overwrites-A*/} + case 127: /* joinop ::= JOIN_KW nm nm JOIN */ +{yymsp[-3].minor.yy144 = sqlite3JoinType(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0);/*X-overwrites-A*/} break; - case 126: /* on_using ::= ON expr */ -{yymsp[-1].minor.yy561.pOn = yymsp[0].minor.yy528; yymsp[-1].minor.yy561.pUsing = 0;} + case 128: /* on_using ::= ON expr */ +{yymsp[-1].minor.yy269.pOn = yymsp[0].minor.yy454; yymsp[-1].minor.yy269.pUsing = 0;} break; - case 127: /* on_using ::= USING LP idlist RP */ -{yymsp[-3].minor.yy561.pOn = 0; yymsp[-3].minor.yy561.pUsing = yymsp[-1].minor.yy254;} + case 129: /* on_using ::= USING LP idlist RP */ +{yymsp[-3].minor.yy269.pOn = 0; yymsp[-3].minor.yy269.pUsing = yymsp[-1].minor.yy132;} break; - case 128: /* on_using ::= */ -{yymsp[1].minor.yy561.pOn = 0; yymsp[1].minor.yy561.pUsing = 0;} + case 130: /* on_using ::= */ +{yymsp[1].minor.yy269.pOn = 0; yymsp[1].minor.yy269.pUsing = 0;} break; - case 130: /* indexed_by ::= INDEXED BY nm */ + case 132: /* indexed_by ::= INDEXED BY nm */ {yymsp[-2].minor.yy0 = yymsp[0].minor.yy0;} break; - case 131: /* indexed_by ::= NOT INDEXED */ + case 133: /* indexed_by ::= NOT INDEXED */ {yymsp[-1].minor.yy0.z=0; yymsp[-1].minor.yy0.n=1;} break; - case 133: /* orderby_opt ::= ORDER BY sortlist */ - case 143: /* groupby_opt ::= GROUP BY nexprlist */ yytestcase(yyruleno==143); -{yymsp[-2].minor.yy322 = yymsp[0].minor.yy322;} + case 135: /* orderby_opt ::= ORDER BY sortlist */ + case 145: /* groupby_opt ::= GROUP BY nexprlist */ yytestcase(yyruleno==145); +{yymsp[-2].minor.yy14 = yymsp[0].minor.yy14;} break; - case 134: /* sortlist ::= sortlist COMMA expr sortorder nulls */ + case 136: /* sortlist ::= sortlist COMMA expr sortorder nulls */ { - yymsp[-4].minor.yy322 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy322,yymsp[-2].minor.yy528); - sqlite3ExprListSetSortOrder(yymsp[-4].minor.yy322,yymsp[-1].minor.yy394,yymsp[0].minor.yy394); + yymsp[-4].minor.yy14 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy14,yymsp[-2].minor.yy454); + sqlite3ExprListSetSortOrder(yymsp[-4].minor.yy14,yymsp[-1].minor.yy144,yymsp[0].minor.yy144); } break; - case 135: /* sortlist ::= expr sortorder nulls */ + case 137: /* sortlist ::= expr sortorder nulls */ { - yymsp[-2].minor.yy322 = sqlite3ExprListAppend(pParse,0,yymsp[-2].minor.yy528); /*A-overwrites-Y*/ - sqlite3ExprListSetSortOrder(yymsp[-2].minor.yy322,yymsp[-1].minor.yy394,yymsp[0].minor.yy394); + yymsp[-2].minor.yy14 = sqlite3ExprListAppend(pParse,0,yymsp[-2].minor.yy454); /*A-overwrites-Y*/ + sqlite3ExprListSetSortOrder(yymsp[-2].minor.yy14,yymsp[-1].minor.yy144,yymsp[0].minor.yy144); } break; - case 136: /* sortorder ::= ASC */ -{yymsp[0].minor.yy394 = SQLITE_SO_ASC;} + case 138: /* sortorder ::= ASC */ +{yymsp[0].minor.yy144 = SQLITE_SO_ASC;} break; - case 137: /* sortorder ::= DESC */ -{yymsp[0].minor.yy394 = SQLITE_SO_DESC;} + case 139: /* sortorder ::= DESC */ +{yymsp[0].minor.yy144 = SQLITE_SO_DESC;} break; - case 138: /* sortorder ::= */ - case 141: /* nulls ::= */ yytestcase(yyruleno==141); -{yymsp[1].minor.yy394 = SQLITE_SO_UNDEFINED;} + case 140: /* sortorder ::= */ + case 143: /* nulls ::= */ yytestcase(yyruleno==143); +{yymsp[1].minor.yy144 = SQLITE_SO_UNDEFINED;} break; - case 139: /* nulls ::= NULLS FIRST */ -{yymsp[-1].minor.yy394 = SQLITE_SO_ASC;} + case 141: /* nulls ::= NULLS FIRST */ +{yymsp[-1].minor.yy144 = SQLITE_SO_ASC;} break; - case 140: /* nulls ::= NULLS LAST */ -{yymsp[-1].minor.yy394 = SQLITE_SO_DESC;} + case 142: /* nulls ::= NULLS LAST */ +{yymsp[-1].minor.yy144 = SQLITE_SO_DESC;} break; - case 144: /* having_opt ::= */ - case 146: /* limit_opt ::= */ yytestcase(yyruleno==146); - case 151: /* where_opt ::= */ yytestcase(yyruleno==151); - case 153: /* where_opt_ret ::= */ yytestcase(yyruleno==153); - 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;} + case 146: /* having_opt ::= */ + case 148: /* limit_opt ::= */ yytestcase(yyruleno==148); + case 153: /* where_opt ::= */ yytestcase(yyruleno==153); + case 155: /* where_opt_ret ::= */ yytestcase(yyruleno==155); + case 232: /* case_else ::= */ yytestcase(yyruleno==232); + case 233: /* case_operand ::= */ yytestcase(yyruleno==233); + case 252: /* vinto ::= */ yytestcase(yyruleno==252); +{yymsp[1].minor.yy454 = 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 229: /* case_else ::= ELSE expr */ yytestcase(yyruleno==229); - case 249: /* vinto ::= INTO expr */ yytestcase(yyruleno==249); -{yymsp[-1].minor.yy528 = yymsp[0].minor.yy528;} + case 147: /* having_opt ::= HAVING expr */ + case 154: /* where_opt ::= WHERE expr */ yytestcase(yyruleno==154); + case 156: /* where_opt_ret ::= WHERE expr */ yytestcase(yyruleno==156); + case 231: /* case_else ::= ELSE expr */ yytestcase(yyruleno==231); + case 251: /* vinto ::= INTO expr */ yytestcase(yyruleno==251); +{yymsp[-1].minor.yy454 = yymsp[0].minor.yy454;} break; - case 147: /* limit_opt ::= LIMIT expr */ -{yymsp[-1].minor.yy528 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy528,0);} + case 149: /* limit_opt ::= LIMIT expr */ +{yymsp[-1].minor.yy454 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy454,0);} break; - case 148: /* limit_opt ::= LIMIT expr OFFSET expr */ -{yymsp[-3].minor.yy528 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[-2].minor.yy528,yymsp[0].minor.yy528);} + case 150: /* limit_opt ::= LIMIT expr OFFSET expr */ +{yymsp[-3].minor.yy454 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[-2].minor.yy454,yymsp[0].minor.yy454);} break; - case 149: /* limit_opt ::= LIMIT expr COMMA expr */ -{yymsp[-3].minor.yy528 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy528,yymsp[-2].minor.yy528);} + case 151: /* limit_opt ::= LIMIT expr COMMA expr */ +{yymsp[-3].minor.yy454 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy454,yymsp[-2].minor.yy454);} break; - case 150: /* cmd ::= with DELETE FROM xfullname indexed_opt where_opt_ret */ + case 152: /* cmd ::= with DELETE FROM xfullname indexed_opt where_opt_ret */ { - sqlite3SrcListIndexedBy(pParse, yymsp[-2].minor.yy131, &yymsp[-1].minor.yy0); - sqlite3DeleteFrom(pParse,yymsp[-2].minor.yy131,yymsp[0].minor.yy528,0,0); + sqlite3SrcListIndexedBy(pParse, yymsp[-2].minor.yy203, &yymsp[-1].minor.yy0); + sqlite3DeleteFrom(pParse,yymsp[-2].minor.yy203,yymsp[0].minor.yy454,0,0); } break; - case 155: /* where_opt_ret ::= RETURNING selcollist */ -{sqlite3AddReturning(pParse,yymsp[0].minor.yy322); yymsp[-1].minor.yy528 = 0;} + case 157: /* where_opt_ret ::= RETURNING selcollist */ +{sqlite3AddReturning(pParse,yymsp[0].minor.yy14); yymsp[-1].minor.yy454 = 0;} break; - case 156: /* where_opt_ret ::= WHERE expr RETURNING selcollist */ -{sqlite3AddReturning(pParse,yymsp[0].minor.yy322); yymsp[-3].minor.yy528 = yymsp[-2].minor.yy528;} + case 158: /* where_opt_ret ::= WHERE expr RETURNING selcollist */ +{sqlite3AddReturning(pParse,yymsp[0].minor.yy14); yymsp[-3].minor.yy454 = yymsp[-2].minor.yy454;} break; - case 157: /* cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt_ret */ + case 159: /* cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt_ret */ { - sqlite3SrcListIndexedBy(pParse, yymsp[-5].minor.yy131, &yymsp[-4].minor.yy0); - sqlite3ExprListCheckLength(pParse,yymsp[-2].minor.yy322,"set list"); - if( yymsp[-1].minor.yy131 ){ - SrcList *pFromClause = yymsp[-1].minor.yy131; + sqlite3SrcListIndexedBy(pParse, yymsp[-5].minor.yy203, &yymsp[-4].minor.yy0); + sqlite3ExprListCheckLength(pParse,yymsp[-2].minor.yy14,"set list"); + if( yymsp[-1].minor.yy203 ){ + SrcList *pFromClause = yymsp[-1].minor.yy203; if( pFromClause->nSrc>1 ){ Select *pSubquery; Token as; @@ -175355,92 +176754,92 @@ static YYACTIONTYPE yy_reduce( as.z = 0; pFromClause = sqlite3SrcListAppendFromTerm(pParse,0,0,0,&as,pSubquery,0); } - yymsp[-5].minor.yy131 = sqlite3SrcListAppendList(pParse, yymsp[-5].minor.yy131, pFromClause); + yymsp[-5].minor.yy203 = sqlite3SrcListAppendList(pParse, yymsp[-5].minor.yy203, pFromClause); } - sqlite3Update(pParse,yymsp[-5].minor.yy131,yymsp[-2].minor.yy322,yymsp[0].minor.yy528,yymsp[-6].minor.yy394,0,0,0); + sqlite3Update(pParse,yymsp[-5].minor.yy203,yymsp[-2].minor.yy14,yymsp[0].minor.yy454,yymsp[-6].minor.yy144,0,0,0); } break; - case 158: /* setlist ::= setlist COMMA nm EQ expr */ + case 160: /* setlist ::= setlist COMMA nm EQ expr */ { - yymsp[-4].minor.yy322 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy322, yymsp[0].minor.yy528); - sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy322, &yymsp[-2].minor.yy0, 1); + yymsp[-4].minor.yy14 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy14, yymsp[0].minor.yy454); + sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy14, &yymsp[-2].minor.yy0, 1); } break; - case 159: /* setlist ::= setlist COMMA LP idlist RP EQ expr */ + case 161: /* setlist ::= setlist COMMA LP idlist RP EQ expr */ { - yymsp[-6].minor.yy322 = sqlite3ExprListAppendVector(pParse, yymsp[-6].minor.yy322, yymsp[-3].minor.yy254, yymsp[0].minor.yy528); + yymsp[-6].minor.yy14 = sqlite3ExprListAppendVector(pParse, yymsp[-6].minor.yy14, yymsp[-3].minor.yy132, yymsp[0].minor.yy454); } break; - case 160: /* setlist ::= nm EQ expr */ + case 162: /* setlist ::= nm EQ expr */ { - yylhsminor.yy322 = sqlite3ExprListAppend(pParse, 0, yymsp[0].minor.yy528); - sqlite3ExprListSetName(pParse, yylhsminor.yy322, &yymsp[-2].minor.yy0, 1); + yylhsminor.yy14 = sqlite3ExprListAppend(pParse, 0, yymsp[0].minor.yy454); + sqlite3ExprListSetName(pParse, yylhsminor.yy14, &yymsp[-2].minor.yy0, 1); } - yymsp[-2].minor.yy322 = yylhsminor.yy322; + yymsp[-2].minor.yy14 = yylhsminor.yy14; break; - case 161: /* setlist ::= LP idlist RP EQ expr */ + case 163: /* setlist ::= LP idlist RP EQ expr */ { - yymsp[-4].minor.yy322 = sqlite3ExprListAppendVector(pParse, 0, yymsp[-3].minor.yy254, yymsp[0].minor.yy528); + yymsp[-4].minor.yy14 = sqlite3ExprListAppendVector(pParse, 0, yymsp[-3].minor.yy132, yymsp[0].minor.yy454); } break; - case 162: /* cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */ + case 164: /* cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */ { - sqlite3Insert(pParse, yymsp[-3].minor.yy131, yymsp[-1].minor.yy47, yymsp[-2].minor.yy254, yymsp[-5].minor.yy394, yymsp[0].minor.yy444); + sqlite3Insert(pParse, yymsp[-3].minor.yy203, yymsp[-1].minor.yy555, yymsp[-2].minor.yy132, yymsp[-5].minor.yy144, yymsp[0].minor.yy122); } break; - case 163: /* cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES returning */ + case 165: /* cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES returning */ { - sqlite3Insert(pParse, yymsp[-4].minor.yy131, 0, yymsp[-3].minor.yy254, yymsp[-6].minor.yy394, 0); + sqlite3Insert(pParse, yymsp[-4].minor.yy203, 0, yymsp[-3].minor.yy132, yymsp[-6].minor.yy144, 0); } break; - case 164: /* upsert ::= */ -{ yymsp[1].minor.yy444 = 0; } + case 166: /* upsert ::= */ +{ yymsp[1].minor.yy122 = 0; } break; - case 165: /* upsert ::= RETURNING selcollist */ -{ yymsp[-1].minor.yy444 = 0; sqlite3AddReturning(pParse,yymsp[0].minor.yy322); } + case 167: /* upsert ::= RETURNING selcollist */ +{ yymsp[-1].minor.yy122 = 0; sqlite3AddReturning(pParse,yymsp[0].minor.yy14); } break; - case 166: /* upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert */ -{ yymsp[-11].minor.yy444 = sqlite3UpsertNew(pParse->db,yymsp[-8].minor.yy322,yymsp[-6].minor.yy528,yymsp[-2].minor.yy322,yymsp[-1].minor.yy528,yymsp[0].minor.yy444);} + case 168: /* upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert */ +{ yymsp[-11].minor.yy122 = sqlite3UpsertNew(pParse->db,yymsp[-8].minor.yy14,yymsp[-6].minor.yy454,yymsp[-2].minor.yy14,yymsp[-1].minor.yy454,yymsp[0].minor.yy122);} break; - case 167: /* upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert */ -{ yymsp[-8].minor.yy444 = sqlite3UpsertNew(pParse->db,yymsp[-5].minor.yy322,yymsp[-3].minor.yy528,0,0,yymsp[0].minor.yy444); } + case 169: /* upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert */ +{ yymsp[-8].minor.yy122 = sqlite3UpsertNew(pParse->db,yymsp[-5].minor.yy14,yymsp[-3].minor.yy454,0,0,yymsp[0].minor.yy122); } break; - case 168: /* upsert ::= ON CONFLICT DO NOTHING returning */ -{ yymsp[-4].minor.yy444 = sqlite3UpsertNew(pParse->db,0,0,0,0,0); } + case 170: /* upsert ::= ON CONFLICT DO NOTHING returning */ +{ yymsp[-4].minor.yy122 = sqlite3UpsertNew(pParse->db,0,0,0,0,0); } break; - case 169: /* upsert ::= ON CONFLICT DO UPDATE SET setlist where_opt returning */ -{ yymsp[-7].minor.yy444 = sqlite3UpsertNew(pParse->db,0,0,yymsp[-2].minor.yy322,yymsp[-1].minor.yy528,0);} + case 171: /* upsert ::= ON CONFLICT DO UPDATE SET setlist where_opt returning */ +{ yymsp[-7].minor.yy122 = sqlite3UpsertNew(pParse->db,0,0,yymsp[-2].minor.yy14,yymsp[-1].minor.yy454,0);} break; - case 170: /* returning ::= RETURNING selcollist */ -{sqlite3AddReturning(pParse,yymsp[0].minor.yy322);} + case 172: /* returning ::= RETURNING selcollist */ +{sqlite3AddReturning(pParse,yymsp[0].minor.yy14);} break; - case 173: /* idlist_opt ::= */ -{yymsp[1].minor.yy254 = 0;} + case 175: /* idlist_opt ::= */ +{yymsp[1].minor.yy132 = 0;} break; - case 174: /* idlist_opt ::= LP idlist RP */ -{yymsp[-2].minor.yy254 = yymsp[-1].minor.yy254;} + case 176: /* idlist_opt ::= LP idlist RP */ +{yymsp[-2].minor.yy132 = yymsp[-1].minor.yy132;} break; - case 175: /* idlist ::= idlist COMMA nm */ -{yymsp[-2].minor.yy254 = sqlite3IdListAppend(pParse,yymsp[-2].minor.yy254,&yymsp[0].minor.yy0);} + case 177: /* idlist ::= idlist COMMA nm */ +{yymsp[-2].minor.yy132 = sqlite3IdListAppend(pParse,yymsp[-2].minor.yy132,&yymsp[0].minor.yy0);} break; - case 176: /* idlist ::= nm */ -{yymsp[0].minor.yy254 = sqlite3IdListAppend(pParse,0,&yymsp[0].minor.yy0); /*A-overwrites-Y*/} + case 178: /* idlist ::= nm */ +{yymsp[0].minor.yy132 = sqlite3IdListAppend(pParse,0,&yymsp[0].minor.yy0); /*A-overwrites-Y*/} break; - case 177: /* expr ::= LP expr RP */ -{yymsp[-2].minor.yy528 = yymsp[-1].minor.yy528;} + case 179: /* expr ::= LP expr RP */ +{yymsp[-2].minor.yy454 = yymsp[-1].minor.yy454;} break; - case 178: /* expr ::= ID|INDEXED|JOIN_KW */ -{yymsp[0].minor.yy528=tokenExpr(pParse,TK_ID,yymsp[0].minor.yy0); /*A-overwrites-X*/} + case 180: /* expr ::= ID|INDEXED|JOIN_KW */ +{yymsp[0].minor.yy454=tokenExpr(pParse,TK_ID,yymsp[0].minor.yy0); /*A-overwrites-X*/} break; - case 179: /* expr ::= nm DOT nm */ + case 181: /* expr ::= nm DOT nm */ { Expr *temp1 = tokenExpr(pParse,TK_ID,yymsp[-2].minor.yy0); Expr *temp2 = tokenExpr(pParse,TK_ID,yymsp[0].minor.yy0); - yylhsminor.yy528 = sqlite3PExpr(pParse, TK_DOT, temp1, temp2); + yylhsminor.yy454 = sqlite3PExpr(pParse, TK_DOT, temp1, temp2); } - yymsp[-2].minor.yy528 = yylhsminor.yy528; + yymsp[-2].minor.yy454 = yylhsminor.yy454; break; - case 180: /* expr ::= nm DOT nm DOT nm */ + case 182: /* expr ::= nm DOT nm DOT nm */ { Expr *temp1 = tokenExpr(pParse,TK_ID,yymsp[-4].minor.yy0); Expr *temp2 = tokenExpr(pParse,TK_ID,yymsp[-2].minor.yy0); @@ -175449,27 +176848,27 @@ static YYACTIONTYPE yy_reduce( if( IN_RENAME_OBJECT ){ sqlite3RenameTokenRemap(pParse, 0, temp1); } - yylhsminor.yy528 = sqlite3PExpr(pParse, TK_DOT, temp1, temp4); + yylhsminor.yy454 = sqlite3PExpr(pParse, TK_DOT, temp1, temp4); } - yymsp[-4].minor.yy528 = yylhsminor.yy528; + yymsp[-4].minor.yy454 = yylhsminor.yy454; break; - case 181: /* term ::= NULL|FLOAT|BLOB */ - case 182: /* term ::= STRING */ yytestcase(yyruleno==182); -{yymsp[0].minor.yy528=tokenExpr(pParse,yymsp[0].major,yymsp[0].minor.yy0); /*A-overwrites-X*/} + case 183: /* term ::= NULL|FLOAT|BLOB */ + case 184: /* term ::= STRING */ yytestcase(yyruleno==184); +{yymsp[0].minor.yy454=tokenExpr(pParse,yymsp[0].major,yymsp[0].minor.yy0); /*A-overwrites-X*/} break; - case 183: /* term ::= INTEGER */ + case 185: /* term ::= INTEGER */ { - yylhsminor.yy528 = sqlite3ExprAlloc(pParse->db, TK_INTEGER, &yymsp[0].minor.yy0, 1); - if( yylhsminor.yy528 ) yylhsminor.yy528->w.iOfst = (int)(yymsp[0].minor.yy0.z - pParse->zTail); + yylhsminor.yy454 = sqlite3ExprAlloc(pParse->db, TK_INTEGER, &yymsp[0].minor.yy0, 1); + if( yylhsminor.yy454 ) yylhsminor.yy454->w.iOfst = (int)(yymsp[0].minor.yy0.z - pParse->zTail); } - yymsp[0].minor.yy528 = yylhsminor.yy528; + yymsp[0].minor.yy454 = yylhsminor.yy454; break; - case 184: /* expr ::= VARIABLE */ + case 186: /* expr ::= VARIABLE */ { if( !(yymsp[0].minor.yy0.z[0]=='#' && sqlite3Isdigit(yymsp[0].minor.yy0.z[1])) ){ u32 n = yymsp[0].minor.yy0.n; - yymsp[0].minor.yy528 = tokenExpr(pParse, TK_VARIABLE, yymsp[0].minor.yy0); - sqlite3ExprAssignVarNumber(pParse, yymsp[0].minor.yy528, n); + yymsp[0].minor.yy454 = tokenExpr(pParse, TK_VARIABLE, yymsp[0].minor.yy0); + sqlite3ExprAssignVarNumber(pParse, yymsp[0].minor.yy454, n); }else{ /* When doing a nested parse, one can include terms in an expression ** that look like this: #1 #2 ... These terms refer to registers @@ -175478,194 +176877,203 @@ static YYACTIONTYPE yy_reduce( assert( t.n>=2 ); if( pParse->nested==0 ){ sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &t); - yymsp[0].minor.yy528 = 0; + yymsp[0].minor.yy454 = 0; }else{ - yymsp[0].minor.yy528 = sqlite3PExpr(pParse, TK_REGISTER, 0, 0); - if( yymsp[0].minor.yy528 ) sqlite3GetInt32(&t.z[1], &yymsp[0].minor.yy528->iTable); + yymsp[0].minor.yy454 = sqlite3PExpr(pParse, TK_REGISTER, 0, 0); + if( yymsp[0].minor.yy454 ) sqlite3GetInt32(&t.z[1], &yymsp[0].minor.yy454->iTable); } } } break; - case 185: /* expr ::= expr COLLATE ID|STRING */ + case 187: /* expr ::= expr COLLATE ID|STRING */ { - yymsp[-2].minor.yy528 = sqlite3ExprAddCollateToken(pParse, yymsp[-2].minor.yy528, &yymsp[0].minor.yy0, 1); + yymsp[-2].minor.yy454 = sqlite3ExprAddCollateToken(pParse, yymsp[-2].minor.yy454, &yymsp[0].minor.yy0, 1); } break; - case 186: /* expr ::= CAST LP expr AS typetoken RP */ + case 188: /* expr ::= CAST LP expr AS typetoken RP */ { - yymsp[-5].minor.yy528 = sqlite3ExprAlloc(pParse->db, TK_CAST, &yymsp[-1].minor.yy0, 1); - sqlite3ExprAttachSubtrees(pParse->db, yymsp[-5].minor.yy528, yymsp[-3].minor.yy528, 0); + yymsp[-5].minor.yy454 = sqlite3ExprAlloc(pParse->db, TK_CAST, &yymsp[-1].minor.yy0, 1); + sqlite3ExprAttachSubtrees(pParse->db, yymsp[-5].minor.yy454, yymsp[-3].minor.yy454, 0); } break; - case 187: /* expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP */ + case 189: /* expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP */ { - yylhsminor.yy528 = sqlite3ExprFunction(pParse, yymsp[-1].minor.yy322, &yymsp[-4].minor.yy0, yymsp[-2].minor.yy394); + yylhsminor.yy454 = sqlite3ExprFunction(pParse, yymsp[-1].minor.yy14, &yymsp[-4].minor.yy0, yymsp[-2].minor.yy144); } - yymsp[-4].minor.yy528 = yylhsminor.yy528; + yymsp[-4].minor.yy454 = yylhsminor.yy454; break; - case 188: /* expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP */ + case 190: /* 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); + yylhsminor.yy454 = sqlite3ExprFunction(pParse, yymsp[-4].minor.yy14, &yymsp[-7].minor.yy0, yymsp[-5].minor.yy144); + sqlite3ExprAddFunctionOrderBy(pParse, yylhsminor.yy454, yymsp[-1].minor.yy14); } - yymsp[-7].minor.yy528 = yylhsminor.yy528; + yymsp[-7].minor.yy454 = yylhsminor.yy454; break; - case 189: /* expr ::= ID|INDEXED|JOIN_KW LP STAR RP */ + case 191: /* expr ::= ID|INDEXED|JOIN_KW LP STAR RP */ { - yylhsminor.yy528 = sqlite3ExprFunction(pParse, 0, &yymsp[-3].minor.yy0, 0); + yylhsminor.yy454 = sqlite3ExprFunction(pParse, 0, &yymsp[-3].minor.yy0, 0); } - yymsp[-3].minor.yy528 = yylhsminor.yy528; + yymsp[-3].minor.yy454 = yylhsminor.yy454; break; - case 190: /* expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP filter_over */ + case 192: /* 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); + yylhsminor.yy454 = sqlite3ExprFunction(pParse, yymsp[-2].minor.yy14, &yymsp[-5].minor.yy0, yymsp[-3].minor.yy144); + sqlite3WindowAttach(pParse, yylhsminor.yy454, yymsp[0].minor.yy211); } - yymsp[-5].minor.yy528 = yylhsminor.yy528; + yymsp[-5].minor.yy454 = yylhsminor.yy454; break; - case 191: /* expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP filter_over */ + case 193: /* 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); + yylhsminor.yy454 = sqlite3ExprFunction(pParse, yymsp[-5].minor.yy14, &yymsp[-8].minor.yy0, yymsp[-6].minor.yy144); + sqlite3WindowAttach(pParse, yylhsminor.yy454, yymsp[0].minor.yy211); + sqlite3ExprAddFunctionOrderBy(pParse, yylhsminor.yy454, yymsp[-2].minor.yy14); } - yymsp[-8].minor.yy528 = yylhsminor.yy528; + yymsp[-8].minor.yy454 = yylhsminor.yy454; break; - case 192: /* expr ::= ID|INDEXED|JOIN_KW LP STAR RP filter_over */ + case 194: /* 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); + yylhsminor.yy454 = sqlite3ExprFunction(pParse, 0, &yymsp[-4].minor.yy0, 0); + sqlite3WindowAttach(pParse, yylhsminor.yy454, yymsp[0].minor.yy211); } - yymsp[-4].minor.yy528 = yylhsminor.yy528; + yymsp[-4].minor.yy454 = yylhsminor.yy454; break; - case 193: /* term ::= CTIME_KW */ + case 195: /* term ::= CTIME_KW */ { - yylhsminor.yy528 = sqlite3ExprFunction(pParse, 0, &yymsp[0].minor.yy0, 0); + yylhsminor.yy454 = sqlite3ExprFunction(pParse, 0, &yymsp[0].minor.yy0, 0); } - yymsp[0].minor.yy528 = yylhsminor.yy528; + yymsp[0].minor.yy454 = yylhsminor.yy454; break; - case 194: /* expr ::= LP nexprlist COMMA expr RP */ + case 196: /* 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); - if( yymsp[-4].minor.yy528 ){ - yymsp[-4].minor.yy528->x.pList = pList; + ExprList *pList = sqlite3ExprListAppend(pParse, yymsp[-3].minor.yy14, yymsp[-1].minor.yy454); + yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_VECTOR, 0, 0); + if( yymsp[-4].minor.yy454 ){ + yymsp[-4].minor.yy454->x.pList = pList; if( ALWAYS(pList->nExpr) ){ - yymsp[-4].minor.yy528->flags |= pList->a[0].pExpr->flags & EP_Propagate; + yymsp[-4].minor.yy454->flags |= pList->a[0].pExpr->flags & EP_Propagate; } }else{ sqlite3ExprListDelete(pParse->db, pList); } } break; - case 195: /* expr ::= expr AND expr */ -{yymsp[-2].minor.yy528=sqlite3ExprAnd(pParse,yymsp[-2].minor.yy528,yymsp[0].minor.yy528);} + case 197: /* expr ::= expr AND expr */ +{yymsp[-2].minor.yy454=sqlite3ExprAnd(pParse,yymsp[-2].minor.yy454,yymsp[0].minor.yy454);} break; - 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);} + case 198: /* expr ::= expr OR expr */ + case 199: /* expr ::= expr LT|GT|GE|LE expr */ yytestcase(yyruleno==199); + case 200: /* expr ::= expr EQ|NE expr */ yytestcase(yyruleno==200); + case 201: /* expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ yytestcase(yyruleno==201); + case 202: /* expr ::= expr PLUS|MINUS expr */ yytestcase(yyruleno==202); + case 203: /* expr ::= expr STAR|SLASH|REM expr */ yytestcase(yyruleno==203); + case 204: /* expr ::= expr CONCAT expr */ yytestcase(yyruleno==204); +{yymsp[-2].minor.yy454=sqlite3PExpr(pParse,yymsp[-1].major,yymsp[-2].minor.yy454,yymsp[0].minor.yy454);} break; - case 203: /* likeop ::= NOT LIKE_KW|MATCH */ + case 205: /* 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 204: /* expr ::= expr likeop expr */ + case 206: /* expr ::= expr likeop expr */ { ExprList *pList; int bNot = yymsp[-1].minor.yy0.n & 0x80000000; yymsp[-1].minor.yy0.n &= 0x7fffffff; - pList = sqlite3ExprListAppend(pParse,0, yymsp[0].minor.yy528); - pList = sqlite3ExprListAppend(pParse,pList, yymsp[-2].minor.yy528); - yymsp[-2].minor.yy528 = sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy0, 0); - if( bNot ) yymsp[-2].minor.yy528 = sqlite3PExpr(pParse, TK_NOT, yymsp[-2].minor.yy528, 0); - if( yymsp[-2].minor.yy528 ) yymsp[-2].minor.yy528->flags |= EP_InfixFunc; + pList = sqlite3ExprListAppend(pParse,0, yymsp[0].minor.yy454); + pList = sqlite3ExprListAppend(pParse,pList, yymsp[-2].minor.yy454); + yymsp[-2].minor.yy454 = sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy0, 0); + if( bNot ) yymsp[-2].minor.yy454 = sqlite3PExpr(pParse, TK_NOT, yymsp[-2].minor.yy454, 0); + if( yymsp[-2].minor.yy454 ) yymsp[-2].minor.yy454->flags |= EP_InfixFunc; } break; - case 205: /* expr ::= expr likeop expr ESCAPE expr */ + case 207: /* expr ::= expr likeop expr ESCAPE expr */ { ExprList *pList; int bNot = yymsp[-3].minor.yy0.n & 0x80000000; yymsp[-3].minor.yy0.n &= 0x7fffffff; - pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy528); - pList = sqlite3ExprListAppend(pParse,pList, yymsp[-4].minor.yy528); - pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy528); - yymsp[-4].minor.yy528 = sqlite3ExprFunction(pParse, pList, &yymsp[-3].minor.yy0, 0); - if( bNot ) yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy528, 0); - if( yymsp[-4].minor.yy528 ) yymsp[-4].minor.yy528->flags |= EP_InfixFunc; + pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy454); + pList = sqlite3ExprListAppend(pParse,pList, yymsp[-4].minor.yy454); + pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy454); + yymsp[-4].minor.yy454 = sqlite3ExprFunction(pParse, pList, &yymsp[-3].minor.yy0, 0); + if( bNot ) yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy454, 0); + if( yymsp[-4].minor.yy454 ) yymsp[-4].minor.yy454->flags |= EP_InfixFunc; } break; - case 206: /* expr ::= expr ISNULL|NOTNULL */ -{yymsp[-1].minor.yy528 = sqlite3PExpr(pParse,yymsp[0].major,yymsp[-1].minor.yy528,0);} + case 208: /* expr ::= expr ISNULL|NOTNULL */ +{yymsp[-1].minor.yy454 = sqlite3PExpr(pParse,yymsp[0].major,yymsp[-1].minor.yy454,0);} break; - case 207: /* expr ::= expr NOT NULL */ -{yymsp[-2].minor.yy528 = sqlite3PExpr(pParse,TK_NOTNULL,yymsp[-2].minor.yy528,0);} + case 209: /* expr ::= expr NOT NULL */ +{yymsp[-2].minor.yy454 = sqlite3PExpr(pParse,TK_NOTNULL,yymsp[-2].minor.yy454,0);} break; - case 208: /* expr ::= expr IS expr */ + case 210: /* 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); + yymsp[-2].minor.yy454 = sqlite3PExpr(pParse,TK_IS,yymsp[-2].minor.yy454,yymsp[0].minor.yy454); + binaryToUnaryIfNull(pParse, yymsp[0].minor.yy454, yymsp[-2].minor.yy454, TK_ISNULL); } break; - case 209: /* expr ::= expr IS NOT expr */ + case 211: /* 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); + yymsp[-3].minor.yy454 = sqlite3PExpr(pParse,TK_ISNOT,yymsp[-3].minor.yy454,yymsp[0].minor.yy454); + binaryToUnaryIfNull(pParse, yymsp[0].minor.yy454, yymsp[-3].minor.yy454, TK_NOTNULL); } break; - case 210: /* expr ::= expr IS NOT DISTINCT FROM expr */ + case 212: /* 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); + yymsp[-5].minor.yy454 = sqlite3PExpr(pParse,TK_IS,yymsp[-5].minor.yy454,yymsp[0].minor.yy454); + binaryToUnaryIfNull(pParse, yymsp[0].minor.yy454, yymsp[-5].minor.yy454, TK_ISNULL); } break; - case 211: /* expr ::= expr IS DISTINCT FROM expr */ + case 213: /* 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); + yymsp[-4].minor.yy454 = sqlite3PExpr(pParse,TK_ISNOT,yymsp[-4].minor.yy454,yymsp[0].minor.yy454); + binaryToUnaryIfNull(pParse, yymsp[0].minor.yy454, yymsp[-4].minor.yy454, TK_NOTNULL); } break; - 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*/} + case 214: /* expr ::= NOT expr */ + case 215: /* expr ::= BITNOT expr */ yytestcase(yyruleno==215); +{yymsp[-1].minor.yy454 = sqlite3PExpr(pParse, yymsp[-1].major, yymsp[0].minor.yy454, 0);/*A-overwrites-B*/} break; - case 214: /* expr ::= PLUS|MINUS expr */ + case 216: /* 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*/ + Expr *p = yymsp[0].minor.yy454; + u8 op = yymsp[-1].major + (TK_UPLUS-TK_PLUS); + assert( TK_UPLUS>TK_PLUS ); + assert( TK_UMINUS == TK_MINUS + (TK_UPLUS - TK_PLUS) ); + if( p && p->op==TK_UPLUS ){ + p->op = op; + yymsp[-1].minor.yy454 = p; + }else{ + yymsp[-1].minor.yy454 = sqlite3PExpr(pParse, op, p, 0); + /*A-overwrites-B*/ + } } break; - case 215: /* expr ::= expr PTR expr */ + case 217: /* expr ::= expr PTR expr */ { - ExprList *pList = sqlite3ExprListAppend(pParse, 0, yymsp[-2].minor.yy528); - pList = sqlite3ExprListAppend(pParse, pList, yymsp[0].minor.yy528); - yylhsminor.yy528 = sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy0, 0); + ExprList *pList = sqlite3ExprListAppend(pParse, 0, yymsp[-2].minor.yy454); + pList = sqlite3ExprListAppend(pParse, pList, yymsp[0].minor.yy454); + yylhsminor.yy454 = sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy0, 0); } - yymsp[-2].minor.yy528 = yylhsminor.yy528; + yymsp[-2].minor.yy454 = yylhsminor.yy454; break; - case 216: /* between_op ::= BETWEEN */ - case 219: /* in_op ::= IN */ yytestcase(yyruleno==219); -{yymsp[0].minor.yy394 = 0;} + case 218: /* between_op ::= BETWEEN */ + case 221: /* in_op ::= IN */ yytestcase(yyruleno==221); +{yymsp[0].minor.yy144 = 0;} break; - case 218: /* expr ::= expr between_op expr AND expr */ + case 220: /* expr ::= expr between_op expr AND expr */ { - ExprList *pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy528); - pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy528); - yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_BETWEEN, yymsp[-4].minor.yy528, 0); - if( yymsp[-4].minor.yy528 ){ - yymsp[-4].minor.yy528->x.pList = pList; + ExprList *pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy454); + pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy454); + yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_BETWEEN, yymsp[-4].minor.yy454, 0); + if( yymsp[-4].minor.yy454 ){ + yymsp[-4].minor.yy454->x.pList = pList; }else{ sqlite3ExprListDelete(pParse->db, pList); } - if( yymsp[-3].minor.yy394 ) yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy528, 0); + if( yymsp[-3].minor.yy144 ) yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy454, 0); } break; - case 221: /* expr ::= expr in_op LP exprlist RP */ + case 223: /* expr ::= expr in_op LP exprlist RP */ { - if( yymsp[-1].minor.yy322==0 ){ + if( yymsp[-1].minor.yy14==0 ){ /* Expressions of the form ** ** expr1 IN () @@ -175674,208 +177082,208 @@ static YYACTIONTYPE yy_reduce( ** simplify to constants 0 (false) and 1 (true), respectively, ** regardless of the value of expr1. */ - sqlite3ExprUnmapAndDelete(pParse, yymsp[-4].minor.yy528); - yymsp[-4].minor.yy528 = sqlite3Expr(pParse->db, TK_STRING, yymsp[-3].minor.yy394 ? "true" : "false"); - if( yymsp[-4].minor.yy528 ) sqlite3ExprIdToTrueFalse(yymsp[-4].minor.yy528); + sqlite3ExprUnmapAndDelete(pParse, yymsp[-4].minor.yy454); + yymsp[-4].minor.yy454 = sqlite3Expr(pParse->db, TK_STRING, yymsp[-3].minor.yy144 ? "true" : "false"); + if( yymsp[-4].minor.yy454 ) sqlite3ExprIdToTrueFalse(yymsp[-4].minor.yy454); }else{ - Expr *pRHS = yymsp[-1].minor.yy322->a[0].pExpr; - if( yymsp[-1].minor.yy322->nExpr==1 && sqlite3ExprIsConstant(pRHS) && yymsp[-4].minor.yy528->op!=TK_VECTOR ){ - yymsp[-1].minor.yy322->a[0].pExpr = 0; - sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy322); + Expr *pRHS = yymsp[-1].minor.yy14->a[0].pExpr; + if( yymsp[-1].minor.yy14->nExpr==1 && sqlite3ExprIsConstant(pParse,pRHS) && yymsp[-4].minor.yy454->op!=TK_VECTOR ){ + yymsp[-1].minor.yy14->a[0].pExpr = 0; + sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy14); pRHS = sqlite3PExpr(pParse, TK_UPLUS, pRHS, 0); - yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_EQ, yymsp[-4].minor.yy528, pRHS); - }else if( yymsp[-1].minor.yy322->nExpr==1 && pRHS->op==TK_SELECT ){ - yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy528, 0); - sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy528, pRHS->x.pSelect); + yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_EQ, yymsp[-4].minor.yy454, pRHS); + }else if( yymsp[-1].minor.yy14->nExpr==1 && pRHS->op==TK_SELECT ){ + yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy454, 0); + sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy454, pRHS->x.pSelect); pRHS->x.pSelect = 0; - sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy322); + sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy14); }else{ - yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy528, 0); - if( yymsp[-4].minor.yy528==0 ){ - sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy322); - }else if( yymsp[-4].minor.yy528->pLeft->op==TK_VECTOR ){ - int nExpr = yymsp[-4].minor.yy528->pLeft->x.pList->nExpr; - Select *pSelectRHS = sqlite3ExprListToValues(pParse, nExpr, yymsp[-1].minor.yy322); + yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy454, 0); + if( yymsp[-4].minor.yy454==0 ){ + sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy14); + }else if( yymsp[-4].minor.yy454->pLeft->op==TK_VECTOR ){ + int nExpr = yymsp[-4].minor.yy454->pLeft->x.pList->nExpr; + Select *pSelectRHS = sqlite3ExprListToValues(pParse, nExpr, yymsp[-1].minor.yy14); if( pSelectRHS ){ parserDoubleLinkSelect(pParse, pSelectRHS); - sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy528, pSelectRHS); + sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy454, pSelectRHS); } }else{ - yymsp[-4].minor.yy528->x.pList = yymsp[-1].minor.yy322; - sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy528); + yymsp[-4].minor.yy454->x.pList = yymsp[-1].minor.yy14; + sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy454); } } - if( yymsp[-3].minor.yy394 ) yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy528, 0); + if( yymsp[-3].minor.yy144 ) yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy454, 0); } } break; - case 222: /* expr ::= LP select RP */ + case 224: /* expr ::= LP select RP */ { - yymsp[-2].minor.yy528 = sqlite3PExpr(pParse, TK_SELECT, 0, 0); - sqlite3PExprAddSelect(pParse, yymsp[-2].minor.yy528, yymsp[-1].minor.yy47); + yymsp[-2].minor.yy454 = sqlite3PExpr(pParse, TK_SELECT, 0, 0); + sqlite3PExprAddSelect(pParse, yymsp[-2].minor.yy454, yymsp[-1].minor.yy555); } break; - case 223: /* expr ::= expr in_op LP select RP */ + case 225: /* 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); + yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy454, 0); + sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy454, yymsp[-1].minor.yy555); + if( yymsp[-3].minor.yy144 ) yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy454, 0); } break; - case 224: /* expr ::= expr in_op nm dbnm paren_exprlist */ + case 226: /* 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); - if( yymsp[0].minor.yy322 ) sqlite3SrcListFuncArgs(pParse, pSelect ? pSrc : 0, yymsp[0].minor.yy322); - yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy528, 0); - sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy528, pSelect); - if( yymsp[-3].minor.yy394 ) yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy528, 0); + if( yymsp[0].minor.yy14 ) sqlite3SrcListFuncArgs(pParse, pSelect ? pSrc : 0, yymsp[0].minor.yy14); + yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy454, 0); + sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy454, pSelect); + if( yymsp[-3].minor.yy144 ) yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy454, 0); } break; - case 225: /* expr ::= EXISTS LP select RP */ + case 227: /* 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); + p = yymsp[-3].minor.yy454 = sqlite3PExpr(pParse, TK_EXISTS, 0, 0); + sqlite3PExprAddSelect(pParse, p, yymsp[-1].minor.yy555); } break; - case 226: /* expr ::= CASE case_operand case_exprlist case_else END */ + case 228: /* 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 ){ - yymsp[-4].minor.yy528->x.pList = yymsp[-1].minor.yy528 ? sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy322,yymsp[-1].minor.yy528) : yymsp[-2].minor.yy322; - sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy528); + yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_CASE, yymsp[-3].minor.yy454, 0); + if( yymsp[-4].minor.yy454 ){ + yymsp[-4].minor.yy454->x.pList = yymsp[-1].minor.yy454 ? sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy14,yymsp[-1].minor.yy454) : yymsp[-2].minor.yy14; + sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy454); }else{ - sqlite3ExprListDelete(pParse->db, yymsp[-2].minor.yy322); - sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy528); + sqlite3ExprListDelete(pParse->db, yymsp[-2].minor.yy14); + sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy454); } } break; - case 227: /* case_exprlist ::= case_exprlist WHEN expr THEN expr */ + case 229: /* 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); + yymsp[-4].minor.yy14 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy14, yymsp[-2].minor.yy454); + yymsp[-4].minor.yy14 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy14, yymsp[0].minor.yy454); } break; - case 228: /* case_exprlist ::= WHEN expr THEN expr */ + case 230: /* 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); + yymsp[-3].minor.yy14 = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy454); + yymsp[-3].minor.yy14 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy14, yymsp[0].minor.yy454); } break; - case 233: /* nexprlist ::= nexprlist COMMA expr */ -{yymsp[-2].minor.yy322 = sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy322,yymsp[0].minor.yy528);} + case 235: /* nexprlist ::= nexprlist COMMA expr */ +{yymsp[-2].minor.yy14 = sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy14,yymsp[0].minor.yy454);} break; - case 234: /* nexprlist ::= expr */ -{yymsp[0].minor.yy322 = sqlite3ExprListAppend(pParse,0,yymsp[0].minor.yy528); /*A-overwrites-Y*/} + case 236: /* nexprlist ::= expr */ +{yymsp[0].minor.yy14 = sqlite3ExprListAppend(pParse,0,yymsp[0].minor.yy454); /*A-overwrites-Y*/} break; - 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;} + case 238: /* paren_exprlist ::= LP exprlist RP */ + case 243: /* eidlist_opt ::= LP eidlist RP */ yytestcase(yyruleno==243); +{yymsp[-2].minor.yy14 = yymsp[-1].minor.yy14;} break; - case 237: /* cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ + case 239: /* 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, - &yymsp[-11].minor.yy0, yymsp[0].minor.yy528, SQLITE_SO_ASC, yymsp[-8].minor.yy394, SQLITE_IDXTYPE_APPDEF); + sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,0), yymsp[-2].minor.yy14, yymsp[-10].minor.yy144, + &yymsp[-11].minor.yy0, yymsp[0].minor.yy454, SQLITE_SO_ASC, yymsp[-8].minor.yy144, SQLITE_IDXTYPE_APPDEF); if( IN_RENAME_OBJECT && pParse->pNewIndex ){ sqlite3RenameTokenMap(pParse, pParse->pNewIndex->zName, &yymsp[-4].minor.yy0); } } break; - case 238: /* uniqueflag ::= UNIQUE */ - case 280: /* raisetype ::= ABORT */ yytestcase(yyruleno==280); -{yymsp[0].minor.yy394 = OE_Abort;} + case 240: /* uniqueflag ::= UNIQUE */ + case 282: /* raisetype ::= ABORT */ yytestcase(yyruleno==282); +{yymsp[0].minor.yy144 = OE_Abort;} break; - case 239: /* uniqueflag ::= */ -{yymsp[1].minor.yy394 = OE_None;} + case 241: /* uniqueflag ::= */ +{yymsp[1].minor.yy144 = OE_None;} break; - case 242: /* eidlist ::= eidlist COMMA nm collate sortorder */ + case 244: /* 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); + yymsp[-4].minor.yy14 = parserAddExprIdListTerm(pParse, yymsp[-4].minor.yy14, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy144, yymsp[0].minor.yy144); } break; - case 243: /* eidlist ::= nm collate sortorder */ + case 245: /* 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*/ + yymsp[-2].minor.yy14 = parserAddExprIdListTerm(pParse, 0, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy144, yymsp[0].minor.yy144); /*A-overwrites-Y*/ } break; - case 246: /* cmd ::= DROP INDEX ifexists fullname */ -{sqlite3DropIndex(pParse, yymsp[0].minor.yy131, yymsp[-1].minor.yy394);} + case 248: /* cmd ::= DROP INDEX ifexists fullname */ +{sqlite3DropIndex(pParse, yymsp[0].minor.yy203, yymsp[-1].minor.yy144);} break; - case 247: /* cmd ::= VACUUM vinto */ -{sqlite3Vacuum(pParse,0,yymsp[0].minor.yy528);} + case 249: /* cmd ::= VACUUM vinto */ +{sqlite3Vacuum(pParse,0,yymsp[0].minor.yy454);} break; - case 248: /* cmd ::= VACUUM nm vinto */ -{sqlite3Vacuum(pParse,&yymsp[-1].minor.yy0,yymsp[0].minor.yy528);} + case 250: /* cmd ::= VACUUM nm vinto */ +{sqlite3Vacuum(pParse,&yymsp[-1].minor.yy0,yymsp[0].minor.yy454);} break; - case 251: /* cmd ::= PRAGMA nm dbnm */ + case 253: /* cmd ::= PRAGMA nm dbnm */ {sqlite3Pragma(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,0,0);} break; - case 252: /* cmd ::= PRAGMA nm dbnm EQ nmnum */ + case 254: /* cmd ::= PRAGMA nm dbnm EQ nmnum */ {sqlite3Pragma(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0,0);} break; - case 253: /* cmd ::= PRAGMA nm dbnm LP nmnum RP */ + case 255: /* cmd ::= PRAGMA nm dbnm LP nmnum RP */ {sqlite3Pragma(pParse,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-1].minor.yy0,0);} break; - case 254: /* cmd ::= PRAGMA nm dbnm EQ minus_num */ + case 256: /* cmd ::= PRAGMA nm dbnm EQ minus_num */ {sqlite3Pragma(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0,1);} break; - case 255: /* cmd ::= PRAGMA nm dbnm LP minus_num RP */ + case 257: /* 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 258: /* cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ + case 260: /* cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ { Token all; all.z = yymsp[-3].minor.yy0.z; all.n = (int)(yymsp[0].minor.yy0.z - yymsp[-3].minor.yy0.z) + yymsp[0].minor.yy0.n; - sqlite3FinishTrigger(pParse, yymsp[-1].minor.yy33, &all); + sqlite3FinishTrigger(pParse, yymsp[-1].minor.yy427, &all); } break; - case 259: /* trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ + case 261: /* 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); + sqlite3BeginTrigger(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, yymsp[-5].minor.yy144, yymsp[-4].minor.yy286.a, yymsp[-4].minor.yy286.b, yymsp[-2].minor.yy203, yymsp[0].minor.yy454, yymsp[-10].minor.yy144, yymsp[-8].minor.yy144); yymsp[-10].minor.yy0 = (yymsp[-6].minor.yy0.n==0?yymsp[-7].minor.yy0:yymsp[-6].minor.yy0); /*A-overwrites-T*/ } break; - case 260: /* trigger_time ::= BEFORE|AFTER */ -{ yymsp[0].minor.yy394 = yymsp[0].major; /*A-overwrites-X*/ } + case 262: /* trigger_time ::= BEFORE|AFTER */ +{ yymsp[0].minor.yy144 = yymsp[0].major; /*A-overwrites-X*/ } break; - case 261: /* trigger_time ::= INSTEAD OF */ -{ yymsp[-1].minor.yy394 = TK_INSTEAD;} + case 263: /* trigger_time ::= INSTEAD OF */ +{ yymsp[-1].minor.yy144 = TK_INSTEAD;} break; - case 262: /* trigger_time ::= */ -{ yymsp[1].minor.yy394 = TK_BEFORE; } + case 264: /* trigger_time ::= */ +{ yymsp[1].minor.yy144 = TK_BEFORE; } break; - 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;} + case 265: /* trigger_event ::= DELETE|INSERT */ + case 266: /* trigger_event ::= UPDATE */ yytestcase(yyruleno==266); +{yymsp[0].minor.yy286.a = yymsp[0].major; /*A-overwrites-X*/ yymsp[0].minor.yy286.b = 0;} break; - case 265: /* trigger_event ::= UPDATE OF idlist */ -{yymsp[-2].minor.yy180.a = TK_UPDATE; yymsp[-2].minor.yy180.b = yymsp[0].minor.yy254;} + case 267: /* trigger_event ::= UPDATE OF idlist */ +{yymsp[-2].minor.yy286.a = TK_UPDATE; yymsp[-2].minor.yy286.b = yymsp[0].minor.yy132;} break; - case 266: /* when_clause ::= */ - case 285: /* key_opt ::= */ yytestcase(yyruleno==285); -{ yymsp[1].minor.yy528 = 0; } + case 268: /* when_clause ::= */ + case 287: /* key_opt ::= */ yytestcase(yyruleno==287); +{ yymsp[1].minor.yy454 = 0; } break; - case 267: /* when_clause ::= WHEN expr */ - case 286: /* key_opt ::= KEY expr */ yytestcase(yyruleno==286); -{ yymsp[-1].minor.yy528 = yymsp[0].minor.yy528; } + case 269: /* when_clause ::= WHEN expr */ + case 288: /* key_opt ::= KEY expr */ yytestcase(yyruleno==288); +{ yymsp[-1].minor.yy454 = yymsp[0].minor.yy454; } break; - case 268: /* trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ + case 270: /* 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; + assert( yymsp[-2].minor.yy427!=0 ); + yymsp[-2].minor.yy427->pLast->pNext = yymsp[-1].minor.yy427; + yymsp[-2].minor.yy427->pLast = yymsp[-1].minor.yy427; } break; - case 269: /* trigger_cmd_list ::= trigger_cmd SEMI */ + case 271: /* trigger_cmd_list ::= trigger_cmd SEMI */ { - assert( yymsp[-1].minor.yy33!=0 ); - yymsp[-1].minor.yy33->pLast = yymsp[-1].minor.yy33; + assert( yymsp[-1].minor.yy427!=0 ); + yymsp[-1].minor.yy427->pLast = yymsp[-1].minor.yy427; } break; - case 270: /* trnm ::= nm DOT nm */ + case 272: /* trnm ::= nm DOT nm */ { yymsp[-2].minor.yy0 = yymsp[0].minor.yy0; sqlite3ErrorMsg(pParse, @@ -175883,367 +177291,377 @@ static YYACTIONTYPE yy_reduce( "statements within triggers"); } break; - case 271: /* tridxby ::= INDEXED BY nm */ + case 273: /* tridxby ::= INDEXED BY nm */ { sqlite3ErrorMsg(pParse, "the INDEXED BY clause is not allowed on UPDATE or DELETE statements " "within triggers"); } break; - case 272: /* tridxby ::= NOT INDEXED */ + case 274: /* tridxby ::= NOT INDEXED */ { sqlite3ErrorMsg(pParse, "the NOT INDEXED clause is not allowed on UPDATE or DELETE statements " "within triggers"); } break; - 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; + case 275: /* trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */ +{yylhsminor.yy427 = sqlite3TriggerUpdateStep(pParse, &yymsp[-6].minor.yy0, yymsp[-2].minor.yy203, yymsp[-3].minor.yy14, yymsp[-1].minor.yy454, yymsp[-7].minor.yy144, yymsp[-8].minor.yy0.z, yymsp[0].minor.yy168);} + yymsp[-8].minor.yy427 = yylhsminor.yy427; break; - case 274: /* trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */ + case 276: /* 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*/ + yylhsminor.yy427 = sqlite3TriggerInsertStep(pParse,&yymsp[-4].minor.yy0,yymsp[-3].minor.yy132,yymsp[-2].minor.yy555,yymsp[-6].minor.yy144,yymsp[-1].minor.yy122,yymsp[-7].minor.yy168,yymsp[0].minor.yy168);/*yylhsminor.yy427-overwrites-yymsp[-6].minor.yy144*/ } - yymsp[-7].minor.yy33 = yylhsminor.yy33; + yymsp[-7].minor.yy427 = yylhsminor.yy427; break; - 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; + case 277: /* trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ +{yylhsminor.yy427 = sqlite3TriggerDeleteStep(pParse, &yymsp[-3].minor.yy0, yymsp[-1].minor.yy454, yymsp[-5].minor.yy0.z, yymsp[0].minor.yy168);} + yymsp[-5].minor.yy427 = yylhsminor.yy427; break; - 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; + case 278: /* trigger_cmd ::= scanpt select scanpt */ +{yylhsminor.yy427 = sqlite3TriggerSelectStep(pParse->db, yymsp[-1].minor.yy555, yymsp[-2].minor.yy168, yymsp[0].minor.yy168); /*yylhsminor.yy427-overwrites-yymsp[-1].minor.yy555*/} + yymsp[-2].minor.yy427 = yylhsminor.yy427; break; - case 277: /* expr ::= RAISE LP IGNORE RP */ + case 279: /* expr ::= RAISE LP IGNORE RP */ { - yymsp[-3].minor.yy528 = sqlite3PExpr(pParse, TK_RAISE, 0, 0); - if( yymsp[-3].minor.yy528 ){ - yymsp[-3].minor.yy528->affExpr = OE_Ignore; + yymsp[-3].minor.yy454 = sqlite3PExpr(pParse, TK_RAISE, 0, 0); + if( yymsp[-3].minor.yy454 ){ + yymsp[-3].minor.yy454->affExpr = OE_Ignore; } } break; - case 278: /* expr ::= RAISE LP raisetype COMMA nm RP */ + case 280: /* 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 ) { - yymsp[-5].minor.yy528->affExpr = (char)yymsp[-3].minor.yy394; + yymsp[-5].minor.yy454 = sqlite3ExprAlloc(pParse->db, TK_RAISE, &yymsp[-1].minor.yy0, 1); + if( yymsp[-5].minor.yy454 ) { + yymsp[-5].minor.yy454->affExpr = (char)yymsp[-3].minor.yy144; } } break; - case 279: /* raisetype ::= ROLLBACK */ -{yymsp[0].minor.yy394 = OE_Rollback;} + case 281: /* raisetype ::= ROLLBACK */ +{yymsp[0].minor.yy144 = OE_Rollback;} break; - case 281: /* raisetype ::= FAIL */ -{yymsp[0].minor.yy394 = OE_Fail;} + case 283: /* raisetype ::= FAIL */ +{yymsp[0].minor.yy144 = OE_Fail;} break; - case 282: /* cmd ::= DROP TRIGGER ifexists fullname */ + case 284: /* cmd ::= DROP TRIGGER ifexists fullname */ { - sqlite3DropTrigger(pParse,yymsp[0].minor.yy131,yymsp[-1].minor.yy394); + sqlite3DropTrigger(pParse,yymsp[0].minor.yy203,yymsp[-1].minor.yy144); } break; - case 283: /* cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ + case 285: /* cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ { - sqlite3Attach(pParse, yymsp[-3].minor.yy528, yymsp[-1].minor.yy528, yymsp[0].minor.yy528); + sqlite3Attach(pParse, yymsp[-3].minor.yy454, yymsp[-1].minor.yy454, yymsp[0].minor.yy454); } break; - case 284: /* cmd ::= DETACH database_kw_opt expr */ + case 286: /* cmd ::= DETACH database_kw_opt expr */ { - sqlite3Detach(pParse, yymsp[0].minor.yy528); + sqlite3Detach(pParse, yymsp[0].minor.yy454); } break; - case 287: /* cmd ::= REINDEX */ + case 289: /* cmd ::= REINDEX */ {sqlite3Reindex(pParse, 0, 0);} break; - case 288: /* cmd ::= REINDEX nm dbnm */ + case 290: /* cmd ::= REINDEX nm dbnm */ {sqlite3Reindex(pParse, &yymsp[-1].minor.yy0, &yymsp[0].minor.yy0);} break; - case 289: /* cmd ::= ANALYZE */ + case 291: /* cmd ::= ANALYZE */ {sqlite3Analyze(pParse, 0, 0);} break; - case 290: /* cmd ::= ANALYZE nm dbnm */ + case 292: /* cmd ::= ANALYZE nm dbnm */ {sqlite3Analyze(pParse, &yymsp[-1].minor.yy0, &yymsp[0].minor.yy0);} break; - case 291: /* cmd ::= ALTER TABLE fullname RENAME TO nm */ + case 293: /* cmd ::= ALTER TABLE fullname RENAME TO nm */ { - sqlite3AlterRenameTable(pParse,yymsp[-3].minor.yy131,&yymsp[0].minor.yy0); + sqlite3AlterRenameTable(pParse,yymsp[-3].minor.yy203,&yymsp[0].minor.yy0); } break; - case 292: /* cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ + case 294: /* 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 293: /* cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */ + case 295: /* cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */ { - sqlite3AlterDropColumn(pParse, yymsp[-3].minor.yy131, &yymsp[0].minor.yy0); + sqlite3AlterDropColumn(pParse, yymsp[-3].minor.yy203, &yymsp[0].minor.yy0); } break; - case 294: /* add_column_fullname ::= fullname */ + case 296: /* add_column_fullname ::= fullname */ { disableLookaside(pParse); - sqlite3AlterBeginAddColumn(pParse, yymsp[0].minor.yy131); + sqlite3AlterBeginAddColumn(pParse, yymsp[0].minor.yy203); } break; - case 295: /* cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ + case 297: /* cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ { - sqlite3AlterRenameColumn(pParse, yymsp[-5].minor.yy131, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0); + sqlite3AlterRenameColumn(pParse, yymsp[-5].minor.yy203, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0); } break; - case 296: /* cmd ::= create_vtab */ + case 298: /* cmd ::= create_vtab */ {sqlite3VtabFinishParse(pParse,0);} break; - case 297: /* cmd ::= create_vtab LP vtabarglist RP */ + case 299: /* cmd ::= create_vtab LP vtabarglist RP */ {sqlite3VtabFinishParse(pParse,&yymsp[0].minor.yy0);} break; - case 298: /* create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ + case 300: /* 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); + sqlite3VtabBeginParse(pParse, &yymsp[-3].minor.yy0, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0, yymsp[-4].minor.yy144); } break; - case 299: /* vtabarg ::= */ + case 301: /* vtabarg ::= */ {sqlite3VtabArgInit(pParse);} break; - case 300: /* vtabargtoken ::= ANY */ - case 301: /* vtabargtoken ::= lp anylist RP */ yytestcase(yyruleno==301); - case 302: /* lp ::= LP */ yytestcase(yyruleno==302); + case 302: /* vtabargtoken ::= ANY */ + case 303: /* vtabargtoken ::= lp anylist RP */ yytestcase(yyruleno==303); + case 304: /* lp ::= LP */ yytestcase(yyruleno==304); {sqlite3VtabArgExtend(pParse,&yymsp[0].minor.yy0);} break; - case 303: /* with ::= WITH wqlist */ - case 304: /* with ::= WITH RECURSIVE wqlist */ yytestcase(yyruleno==304); -{ sqlite3WithPush(pParse, yymsp[0].minor.yy521, 1); } + case 305: /* with ::= WITH wqlist */ + case 306: /* with ::= WITH RECURSIVE wqlist */ yytestcase(yyruleno==306); +{ sqlite3WithPush(pParse, yymsp[0].minor.yy59, 1); } break; - case 305: /* wqas ::= AS */ -{yymsp[0].minor.yy516 = M10d_Any;} + case 307: /* wqas ::= AS */ +{yymsp[0].minor.yy462 = M10d_Any;} break; - case 306: /* wqas ::= AS MATERIALIZED */ -{yymsp[-1].minor.yy516 = M10d_Yes;} + case 308: /* wqas ::= AS MATERIALIZED */ +{yymsp[-1].minor.yy462 = M10d_Yes;} break; - case 307: /* wqas ::= AS NOT MATERIALIZED */ -{yymsp[-2].minor.yy516 = M10d_No;} + case 309: /* wqas ::= AS NOT MATERIALIZED */ +{yymsp[-2].minor.yy462 = M10d_No;} break; - case 308: /* wqitem ::= nm eidlist_opt wqas LP select RP */ + case 310: /* wqitem ::= withnm 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*/ + yymsp[-5].minor.yy67 = sqlite3CteNew(pParse, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy14, yymsp[-1].minor.yy555, yymsp[-3].minor.yy462); /*A-overwrites-X*/ } break; - case 309: /* wqlist ::= wqitem */ + case 311: /* withnm ::= nm */ +{pParse->bHasWith = 1;} + break; + case 312: /* wqlist ::= wqitem */ { - yymsp[0].minor.yy521 = sqlite3WithAdd(pParse, 0, yymsp[0].minor.yy385); /*A-overwrites-X*/ + yymsp[0].minor.yy59 = sqlite3WithAdd(pParse, 0, yymsp[0].minor.yy67); /*A-overwrites-X*/ } break; - case 310: /* wqlist ::= wqlist COMMA wqitem */ + case 313: /* wqlist ::= wqlist COMMA wqitem */ { - yymsp[-2].minor.yy521 = sqlite3WithAdd(pParse, yymsp[-2].minor.yy521, yymsp[0].minor.yy385); + yymsp[-2].minor.yy59 = sqlite3WithAdd(pParse, yymsp[-2].minor.yy59, yymsp[0].minor.yy67); } break; - case 311: /* windowdefn_list ::= windowdefn_list COMMA windowdefn */ + case 314: /* windowdefn_list ::= windowdefn_list COMMA windowdefn */ { - assert( yymsp[0].minor.yy41!=0 ); - sqlite3WindowChain(pParse, yymsp[0].minor.yy41, yymsp[-2].minor.yy41); - yymsp[0].minor.yy41->pNextWin = yymsp[-2].minor.yy41; - yylhsminor.yy41 = yymsp[0].minor.yy41; + assert( yymsp[0].minor.yy211!=0 ); + sqlite3WindowChain(pParse, yymsp[0].minor.yy211, yymsp[-2].minor.yy211); + yymsp[0].minor.yy211->pNextWin = yymsp[-2].minor.yy211; + yylhsminor.yy211 = yymsp[0].minor.yy211; } - yymsp[-2].minor.yy41 = yylhsminor.yy41; + yymsp[-2].minor.yy211 = yylhsminor.yy211; break; - case 312: /* windowdefn ::= nm AS LP window RP */ + case 315: /* 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); + if( ALWAYS(yymsp[-1].minor.yy211) ){ + yymsp[-1].minor.yy211->zName = sqlite3DbStrNDup(pParse->db, yymsp[-4].minor.yy0.z, yymsp[-4].minor.yy0.n); } - yylhsminor.yy41 = yymsp[-1].minor.yy41; + yylhsminor.yy211 = yymsp[-1].minor.yy211; } - yymsp[-4].minor.yy41 = yylhsminor.yy41; + yymsp[-4].minor.yy211 = yylhsminor.yy211; break; - case 313: /* window ::= PARTITION BY nexprlist orderby_opt frame_opt */ + case 316: /* 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); + yymsp[-4].minor.yy211 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy211, yymsp[-2].minor.yy14, yymsp[-1].minor.yy14, 0); } break; - case 314: /* window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */ + case 317: /* 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); + yylhsminor.yy211 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy211, yymsp[-2].minor.yy14, yymsp[-1].minor.yy14, &yymsp[-5].minor.yy0); } - yymsp[-5].minor.yy41 = yylhsminor.yy41; + yymsp[-5].minor.yy211 = yylhsminor.yy211; break; - case 315: /* window ::= ORDER BY sortlist frame_opt */ + case 318: /* window ::= ORDER BY sortlist frame_opt */ { - yymsp[-3].minor.yy41 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, 0, yymsp[-1].minor.yy322, 0); + yymsp[-3].minor.yy211 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy211, 0, yymsp[-1].minor.yy14, 0); } break; - case 316: /* window ::= nm ORDER BY sortlist frame_opt */ + case 319: /* window ::= nm ORDER BY sortlist frame_opt */ { - yylhsminor.yy41 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, 0, yymsp[-1].minor.yy322, &yymsp[-4].minor.yy0); + yylhsminor.yy211 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy211, 0, yymsp[-1].minor.yy14, &yymsp[-4].minor.yy0); } - yymsp[-4].minor.yy41 = yylhsminor.yy41; + yymsp[-4].minor.yy211 = yylhsminor.yy211; break; - case 317: /* window ::= nm frame_opt */ + case 320: /* window ::= nm frame_opt */ { - yylhsminor.yy41 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, 0, 0, &yymsp[-1].minor.yy0); + yylhsminor.yy211 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy211, 0, 0, &yymsp[-1].minor.yy0); } - yymsp[-1].minor.yy41 = yylhsminor.yy41; + yymsp[-1].minor.yy211 = yylhsminor.yy211; break; - case 318: /* frame_opt ::= */ + case 321: /* frame_opt ::= */ { - yymsp[1].minor.yy41 = sqlite3WindowAlloc(pParse, 0, TK_UNBOUNDED, 0, TK_CURRENT, 0, 0); + yymsp[1].minor.yy211 = sqlite3WindowAlloc(pParse, 0, TK_UNBOUNDED, 0, TK_CURRENT, 0, 0); } break; - case 319: /* frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */ + case 322: /* 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); + yylhsminor.yy211 = sqlite3WindowAlloc(pParse, yymsp[-2].minor.yy144, yymsp[-1].minor.yy509.eType, yymsp[-1].minor.yy509.pExpr, TK_CURRENT, 0, yymsp[0].minor.yy462); } - yymsp[-2].minor.yy41 = yylhsminor.yy41; + yymsp[-2].minor.yy211 = yylhsminor.yy211; break; - case 320: /* frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */ + case 323: /* 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); + yylhsminor.yy211 = sqlite3WindowAlloc(pParse, yymsp[-5].minor.yy144, yymsp[-3].minor.yy509.eType, yymsp[-3].minor.yy509.pExpr, yymsp[-1].minor.yy509.eType, yymsp[-1].minor.yy509.pExpr, yymsp[0].minor.yy462); } - yymsp[-5].minor.yy41 = yylhsminor.yy41; + yymsp[-5].minor.yy211 = yylhsminor.yy211; break; - 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; + case 325: /* frame_bound_s ::= frame_bound */ + case 327: /* frame_bound_e ::= frame_bound */ yytestcase(yyruleno==327); +{yylhsminor.yy509 = yymsp[0].minor.yy509;} + yymsp[0].minor.yy509 = yylhsminor.yy509; break; - 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; + case 326: /* frame_bound_s ::= UNBOUNDED PRECEDING */ + case 328: /* frame_bound_e ::= UNBOUNDED FOLLOWING */ yytestcase(yyruleno==328); + case 330: /* frame_bound ::= CURRENT ROW */ yytestcase(yyruleno==330); +{yylhsminor.yy509.eType = yymsp[-1].major; yylhsminor.yy509.pExpr = 0;} + yymsp[-1].minor.yy509 = yylhsminor.yy509; break; - 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; + case 329: /* frame_bound ::= expr PRECEDING|FOLLOWING */ +{yylhsminor.yy509.eType = yymsp[0].major; yylhsminor.yy509.pExpr = yymsp[-1].minor.yy454;} + yymsp[-1].minor.yy509 = yylhsminor.yy509; break; - case 328: /* frame_exclude_opt ::= */ -{yymsp[1].minor.yy516 = 0;} + case 331: /* frame_exclude_opt ::= */ +{yymsp[1].minor.yy462 = 0;} break; - case 329: /* frame_exclude_opt ::= EXCLUDE frame_exclude */ -{yymsp[-1].minor.yy516 = yymsp[0].minor.yy516;} + case 332: /* frame_exclude_opt ::= EXCLUDE frame_exclude */ +{yymsp[-1].minor.yy462 = yymsp[0].minor.yy462;} break; - 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*/} + case 333: /* frame_exclude ::= NO OTHERS */ + case 334: /* frame_exclude ::= CURRENT ROW */ yytestcase(yyruleno==334); +{yymsp[-1].minor.yy462 = yymsp[-1].major; /*A-overwrites-X*/} break; - case 332: /* frame_exclude ::= GROUP|TIES */ -{yymsp[0].minor.yy516 = yymsp[0].major; /*A-overwrites-X*/} + case 335: /* frame_exclude ::= GROUP|TIES */ +{yymsp[0].minor.yy462 = yymsp[0].major; /*A-overwrites-X*/} break; - case 333: /* window_clause ::= WINDOW windowdefn_list */ -{ yymsp[-1].minor.yy41 = yymsp[0].minor.yy41; } + case 336: /* window_clause ::= WINDOW windowdefn_list */ +{ yymsp[-1].minor.yy211 = yymsp[0].minor.yy211; } break; - case 334: /* filter_over ::= filter_clause over_clause */ + case 337: /* filter_over ::= filter_clause over_clause */ { - if( yymsp[0].minor.yy41 ){ - yymsp[0].minor.yy41->pFilter = yymsp[-1].minor.yy528; + if( yymsp[0].minor.yy211 ){ + yymsp[0].minor.yy211->pFilter = yymsp[-1].minor.yy454; }else{ - sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy528); + sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy454); } - yylhsminor.yy41 = yymsp[0].minor.yy41; + yylhsminor.yy211 = yymsp[0].minor.yy211; } - yymsp[-1].minor.yy41 = yylhsminor.yy41; + yymsp[-1].minor.yy211 = yylhsminor.yy211; break; - case 335: /* filter_over ::= over_clause */ + case 338: /* filter_over ::= over_clause */ { - yylhsminor.yy41 = yymsp[0].minor.yy41; + yylhsminor.yy211 = yymsp[0].minor.yy211; } - yymsp[0].minor.yy41 = yylhsminor.yy41; + yymsp[0].minor.yy211 = yylhsminor.yy211; break; - case 336: /* filter_over ::= filter_clause */ + case 339: /* filter_over ::= filter_clause */ { - yylhsminor.yy41 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window)); - if( yylhsminor.yy41 ){ - yylhsminor.yy41->eFrmType = TK_FILTER; - yylhsminor.yy41->pFilter = yymsp[0].minor.yy528; + yylhsminor.yy211 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window)); + if( yylhsminor.yy211 ){ + yylhsminor.yy211->eFrmType = TK_FILTER; + yylhsminor.yy211->pFilter = yymsp[0].minor.yy454; }else{ - sqlite3ExprDelete(pParse->db, yymsp[0].minor.yy528); + sqlite3ExprDelete(pParse->db, yymsp[0].minor.yy454); } } - yymsp[0].minor.yy41 = yylhsminor.yy41; + yymsp[0].minor.yy211 = yylhsminor.yy211; break; - case 337: /* over_clause ::= OVER LP window RP */ + case 340: /* over_clause ::= OVER LP window RP */ { - yymsp[-3].minor.yy41 = yymsp[-1].minor.yy41; - assert( yymsp[-3].minor.yy41!=0 ); + yymsp[-3].minor.yy211 = yymsp[-1].minor.yy211; + assert( yymsp[-3].minor.yy211!=0 ); } break; - case 338: /* over_clause ::= OVER nm */ + case 341: /* over_clause ::= OVER nm */ { - yymsp[-1].minor.yy41 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window)); - if( yymsp[-1].minor.yy41 ){ - yymsp[-1].minor.yy41->zName = sqlite3DbStrNDup(pParse->db, yymsp[0].minor.yy0.z, yymsp[0].minor.yy0.n); + yymsp[-1].minor.yy211 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window)); + if( yymsp[-1].minor.yy211 ){ + yymsp[-1].minor.yy211->zName = sqlite3DbStrNDup(pParse->db, yymsp[0].minor.yy0.z, yymsp[0].minor.yy0.n); } } break; - case 339: /* filter_clause ::= FILTER LP WHERE expr RP */ -{ yymsp[-4].minor.yy528 = yymsp[-1].minor.yy528; } + case 342: /* filter_clause ::= FILTER LP WHERE expr RP */ +{ yymsp[-4].minor.yy454 = yymsp[-1].minor.yy454; } + break; + case 343: /* term ::= QNUMBER */ +{ + yylhsminor.yy454=tokenExpr(pParse,yymsp[0].major,yymsp[0].minor.yy0); + sqlite3DequoteNumber(pParse, yylhsminor.yy454); +} + yymsp[0].minor.yy454 = yylhsminor.yy454; break; default: - /* (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); + /* (344) input ::= cmdlist */ yytestcase(yyruleno==344); + /* (345) cmdlist ::= cmdlist ecmd */ yytestcase(yyruleno==345); + /* (346) cmdlist ::= ecmd (OPTIMIZED OUT) */ assert(yyruleno!=346); + /* (347) ecmd ::= SEMI */ yytestcase(yyruleno==347); + /* (348) ecmd ::= cmdx SEMI */ yytestcase(yyruleno==348); + /* (349) ecmd ::= explain cmdx SEMI (NEVER REDUCES) */ assert(yyruleno!=349); + /* (350) trans_opt ::= */ yytestcase(yyruleno==350); + /* (351) trans_opt ::= TRANSACTION */ yytestcase(yyruleno==351); + /* (352) trans_opt ::= TRANSACTION nm */ yytestcase(yyruleno==352); + /* (353) savepoint_opt ::= SAVEPOINT */ yytestcase(yyruleno==353); + /* (354) savepoint_opt ::= */ yytestcase(yyruleno==354); + /* (355) cmd ::= create_table create_table_args */ yytestcase(yyruleno==355); + /* (356) table_option_set ::= table_option (OPTIMIZED OUT) */ assert(yyruleno!=356); + /* (357) columnlist ::= columnlist COMMA columnname carglist */ yytestcase(yyruleno==357); + /* (358) columnlist ::= columnname carglist */ yytestcase(yyruleno==358); + /* (359) nm ::= ID|INDEXED|JOIN_KW */ yytestcase(yyruleno==359); + /* (360) nm ::= STRING */ yytestcase(yyruleno==360); + /* (361) typetoken ::= typename */ yytestcase(yyruleno==361); + /* (362) typename ::= ID|STRING */ yytestcase(yyruleno==362); + /* (363) signed ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=363); + /* (364) signed ::= minus_num (OPTIMIZED OUT) */ assert(yyruleno!=364); + /* (365) carglist ::= carglist ccons */ yytestcase(yyruleno==365); + /* (366) carglist ::= */ yytestcase(yyruleno==366); + /* (367) ccons ::= NULL onconf */ yytestcase(yyruleno==367); + /* (368) ccons ::= GENERATED ALWAYS AS generated */ yytestcase(yyruleno==368); + /* (369) ccons ::= AS generated */ yytestcase(yyruleno==369); + /* (370) conslist_opt ::= COMMA conslist */ yytestcase(yyruleno==370); + /* (371) conslist ::= conslist tconscomma tcons */ yytestcase(yyruleno==371); + /* (372) conslist ::= tcons (OPTIMIZED OUT) */ assert(yyruleno!=372); + /* (373) tconscomma ::= */ yytestcase(yyruleno==373); + /* (374) defer_subclause_opt ::= defer_subclause (OPTIMIZED OUT) */ assert(yyruleno!=374); + /* (375) resolvetype ::= raisetype (OPTIMIZED OUT) */ assert(yyruleno!=375); + /* (376) selectnowith ::= oneselect (OPTIMIZED OUT) */ assert(yyruleno!=376); + /* (377) oneselect ::= values */ yytestcase(yyruleno==377); + /* (378) sclp ::= selcollist COMMA */ yytestcase(yyruleno==378); + /* (379) as ::= ID|STRING */ yytestcase(yyruleno==379); + /* (380) indexed_opt ::= indexed_by (OPTIMIZED OUT) */ assert(yyruleno!=380); + /* (381) returning ::= */ yytestcase(yyruleno==381); + /* (382) expr ::= term (OPTIMIZED OUT) */ assert(yyruleno!=382); + /* (383) likeop ::= LIKE_KW|MATCH */ yytestcase(yyruleno==383); + /* (384) case_operand ::= expr */ yytestcase(yyruleno==384); + /* (385) exprlist ::= nexprlist */ yytestcase(yyruleno==385); + /* (386) nmnum ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=386); + /* (387) nmnum ::= nm (OPTIMIZED OUT) */ assert(yyruleno!=387); + /* (388) nmnum ::= ON */ yytestcase(yyruleno==388); + /* (389) nmnum ::= DELETE */ yytestcase(yyruleno==389); + /* (390) nmnum ::= DEFAULT */ yytestcase(yyruleno==390); + /* (391) plus_num ::= INTEGER|FLOAT */ yytestcase(yyruleno==391); + /* (392) foreach_clause ::= */ yytestcase(yyruleno==392); + /* (393) foreach_clause ::= FOR EACH ROW */ yytestcase(yyruleno==393); + /* (394) trnm ::= nm */ yytestcase(yyruleno==394); + /* (395) tridxby ::= */ yytestcase(yyruleno==395); + /* (396) database_kw_opt ::= DATABASE */ yytestcase(yyruleno==396); + /* (397) database_kw_opt ::= */ yytestcase(yyruleno==397); + /* (398) kwcolumn_opt ::= */ yytestcase(yyruleno==398); + /* (399) kwcolumn_opt ::= COLUMNKW */ yytestcase(yyruleno==399); + /* (400) vtabarglist ::= vtabarg */ yytestcase(yyruleno==400); + /* (401) vtabarglist ::= vtabarglist COMMA vtabarg */ yytestcase(yyruleno==401); + /* (402) vtabarg ::= vtabarg vtabargtoken */ yytestcase(yyruleno==402); + /* (403) anylist ::= */ yytestcase(yyruleno==403); + /* (404) anylist ::= anylist LP anylist RP */ yytestcase(yyruleno==404); + /* (405) anylist ::= anylist ANY */ yytestcase(yyruleno==405); + /* (406) with ::= */ yytestcase(yyruleno==406); + /* (407) windowdefn_list ::= windowdefn (OPTIMIZED OUT) */ assert(yyruleno!=407); + /* (408) window ::= frame_opt (OPTIMIZED OUT) */ assert(yyruleno!=408); break; /********** End reduce actions ************************************************/ }; @@ -176430,19 +177848,12 @@ SQLITE_PRIVATE void sqlite3Parser( (int)(yypParser->yytos - yypParser->yystack)); } #endif -#if YYSTACKDEPTH>0 if( yypParser->yytos>=yypParser->yystackEnd ){ - yyStackOverflow(yypParser); - break; - } -#else - if( yypParser->yytos>=&yypParser->yystack[yypParser->yystksz-1] ){ if( yyGrowStack(yypParser) ){ yyStackOverflow(yypParser); break; } } -#endif } yyact = yy_reduce(yypParser,yyruleno,yymajor,yyminor sqlite3ParserCTX_PARAM); }else if( yyact <= YY_MAX_SHIFTREDUCE ){ @@ -177513,27 +178924,58 @@ SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *z, int *tokenType){ *tokenType = TK_INTEGER; #ifndef SQLITE_OMIT_HEX_INTEGER if( z[0]=='0' && (z[1]=='x' || z[1]=='X') && sqlite3Isxdigit(z[2]) ){ - for(i=3; sqlite3Isxdigit(z[i]); i++){} - return i; - } + for(i=3; 1; i++){ + if( sqlite3Isxdigit(z[i])==0 ){ + if( z[i]==SQLITE_DIGIT_SEPARATOR ){ + *tokenType = TK_QNUMBER; + }else{ + break; + } + } + } + }else #endif - for(i=0; sqlite3Isdigit(z[i]); i++){} + { + for(i=0; 1; i++){ + if( sqlite3Isdigit(z[i])==0 ){ + if( z[i]==SQLITE_DIGIT_SEPARATOR ){ + *tokenType = TK_QNUMBER; + }else{ + break; + } + } + } #ifndef SQLITE_OMIT_FLOATING_POINT - if( z[i]=='.' ){ - i++; - while( sqlite3Isdigit(z[i]) ){ i++; } - *tokenType = TK_FLOAT; - } - if( (z[i]=='e' || z[i]=='E') && - ( sqlite3Isdigit(z[i+1]) - || ((z[i+1]=='+' || z[i+1]=='-') && sqlite3Isdigit(z[i+2])) - ) - ){ - i += 2; - while( sqlite3Isdigit(z[i]) ){ i++; } - *tokenType = TK_FLOAT; - } + if( z[i]=='.' ){ + if( *tokenType==TK_INTEGER ) *tokenType = TK_FLOAT; + for(i++; 1; i++){ + if( sqlite3Isdigit(z[i])==0 ){ + if( z[i]==SQLITE_DIGIT_SEPARATOR ){ + *tokenType = TK_QNUMBER; + }else{ + break; + } + } + } + } + if( (z[i]=='e' || z[i]=='E') && + ( sqlite3Isdigit(z[i+1]) + || ((z[i+1]=='+' || z[i+1]=='-') && sqlite3Isdigit(z[i+2])) + ) + ){ + if( *tokenType==TK_INTEGER ) *tokenType = TK_FLOAT; + for(i+=2; 1; i++){ + if( sqlite3Isdigit(z[i])==0 ){ + if( z[i]==SQLITE_DIGIT_SEPARATOR ){ + *tokenType = TK_QNUMBER; + }else{ + break; + } + } + } + } #endif + } while( IdChar(z[i]) ){ *tokenType = TK_ILLEGAL; i++; @@ -177698,10 +179140,13 @@ SQLITE_PRIVATE int sqlite3RunParser(Parse *pParse, const char *zSql){ if( tokenType>=TK_WINDOW ){ assert( tokenType==TK_SPACE || tokenType==TK_OVER || tokenType==TK_FILTER || tokenType==TK_ILLEGAL || tokenType==TK_WINDOW + || tokenType==TK_QNUMBER ); #else if( tokenType>=TK_SPACE ){ - assert( tokenType==TK_SPACE || tokenType==TK_ILLEGAL ); + assert( tokenType==TK_SPACE || tokenType==TK_ILLEGAL + || tokenType==TK_QNUMBER + ); #endif /* SQLITE_OMIT_WINDOWFUNC */ if( AtomicLoad(&db->u1.isInterrupted) ){ pParse->rc = SQLITE_INTERRUPT; @@ -177734,7 +179179,7 @@ SQLITE_PRIVATE int sqlite3RunParser(Parse *pParse, const char *zSql){ assert( n==6 ); tokenType = analyzeFilterKeyword((const u8*)&zSql[6], lastTokenParsed); #endif /* SQLITE_OMIT_WINDOWFUNC */ - }else{ + }else if( tokenType!=TK_QNUMBER ){ Token x; x.z = zSql; x.n = n; @@ -188729,22 +190174,24 @@ static int fts3IntegrityMethod( char **pzErr /* Write error message here */ ){ Fts3Table *p = (Fts3Table*)pVtab; - int rc; + int rc = SQLITE_OK; 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 ){ + assert( rc!=SQLITE_CORRUPT_VTAB ); + if( rc==SQLITE_ERROR || (rc&0xFF)==SQLITE_CORRUPT ){ *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 ){ + if( *pzErr ) rc = SQLITE_OK; + }else if( rc==SQLITE_OK && bOk==0 ){ *pzErr = sqlite3_mprintf("malformed inverted index for FTS%d table %s.%s", p->bFts4 ? 4 : 3, zSchema, zTabname); + if( *pzErr==0 ) rc = SQLITE_NOMEM; } sqlite3Fts3SegmentsClose(p); - return SQLITE_OK; + return rc; } @@ -200406,7 +201853,12 @@ SQLITE_PRIVATE int sqlite3Fts3IntegrityCheck(Fts3Table *p, int *pbOk){ sqlite3_finalize(pStmt); } - *pbOk = (rc==SQLITE_OK && cksum1==cksum2); + if( rc==SQLITE_CORRUPT_VTAB ){ + rc = SQLITE_OK; + *pbOk = 0; + }else{ + *pbOk = (rc==SQLITE_OK && cksum1==cksum2); + } return rc; } @@ -201312,7 +202764,7 @@ static void fts3SnippetDetails( } mCover |= mPhrase; - for(j=0; jnToken; j++){ + for(j=0; jnToken && jnSnippet; j++){ mHighlight |= (mPos>>j); } @@ -203973,7 +205425,6 @@ static void jsonAppendRawNZ(JsonString *p, const char *zIn, u32 N){ } } - /* Append formatted text (not to exceed N bytes) to the JsonString. */ static void jsonPrintf(int N, JsonString *p, const char *zFormat, ...){ @@ -204031,6 +205482,40 @@ static void jsonAppendSeparator(JsonString *p){ jsonAppendChar(p, ','); } +/* c is a control character. Append the canonical JSON representation +** of that control character to p. +** +** This routine assumes that the output buffer has already been enlarged +** sufficiently to hold the worst-case encoding plus a nul terminator. +*/ +static void jsonAppendControlChar(JsonString *p, u8 c){ + static const char aSpecial[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 'b', 't', 'n', 0, 'f', 'r', 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + assert( sizeof(aSpecial)==32 ); + assert( aSpecial['\b']=='b' ); + assert( aSpecial['\f']=='f' ); + assert( aSpecial['\n']=='n' ); + assert( aSpecial['\r']=='r' ); + assert( aSpecial['\t']=='t' ); + assert( c>=0 && cnUsed+7 <= p->nAlloc ); + if( aSpecial[c] ){ + p->zBuf[p->nUsed] = '\\'; + p->zBuf[p->nUsed+1] = aSpecial[c]; + p->nUsed += 2; + }else{ + p->zBuf[p->nUsed] = '\\'; + p->zBuf[p->nUsed+1] = 'u'; + p->zBuf[p->nUsed+2] = '0'; + p->zBuf[p->nUsed+3] = '0'; + p->zBuf[p->nUsed+4] = "0123456789abcdef"[c>>4]; + p->zBuf[p->nUsed+5] = "0123456789abcdef"[c&0xf]; + p->nUsed += 6; + } +} + /* Append the N-byte string in zIn to the end of the JsonString string ** under construction. Enclose the string in double-quotes ("...") and ** escape any double-quotes or backslash characters contained within the @@ -204090,35 +205575,14 @@ static void jsonAppendString(JsonString *p, const char *zIn, u32 N){ } c = z[0]; if( c=='"' || c=='\\' ){ - json_simple_escape: 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=='\'' ){ p->zBuf[p->nUsed++] = c; }else{ - static const char aSpecial[] = { - 0, 0, 0, 0, 0, 0, 0, 0, 'b', 't', 'n', 0, 'f', 'r', 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - }; - assert( sizeof(aSpecial)==32 ); - assert( aSpecial['\b']=='b' ); - assert( aSpecial['\f']=='f' ); - assert( aSpecial['\n']=='n' ); - assert( aSpecial['\r']=='r' ); - assert( aSpecial['\t']=='t' ); - assert( c>=0 && cnUsed+N+7 > p->nAlloc) && jsonStringGrow(p,N+7)!=0 ) return; - p->zBuf[p->nUsed++] = '\\'; - p->zBuf[p->nUsed++] = 'u'; - p->zBuf[p->nUsed++] = '0'; - p->zBuf[p->nUsed++] = '0'; - p->zBuf[p->nUsed++] = "0123456789abcdef"[c>>4]; - p->zBuf[p->nUsed++] = "0123456789abcdef"[c&0xf]; + jsonAppendControlChar(p, c); } z++; N--; @@ -204819,7 +206283,10 @@ static u32 jsonbValidityCheck( if( !jsonIsOk[z[j]] && z[j]!='\'' ){ if( z[j]=='"' ){ if( x==JSONB_TEXTJ ) return j+1; - }else if( z[j]!='\\' || j+1>=k ){ + }else if( z[j]<=0x1f ){ + /* Control characters in JSON5 string literals are ok */ + if( x==JSONB_TEXTJ ) return j+1; + }else if( NEVER(z[j]!='\\') || j+1>=k ){ return j+1; }else if( strchr("\"\\/bfnrt",z[j+1])!=0 ){ j++; @@ -205114,9 +206581,14 @@ json_parse_restart: return -1; } }else if( c<=0x1f ){ - /* Control characters are not allowed in strings */ - pParse->iErr = j; - return -1; + if( c==0 ){ + pParse->iErr = j; + return -1; + } + /* Control characters are not allowed in canonical JSON string + ** literals, but are allowed in JSON5 string literals. */ + opcode = JSONB_TEXT5; + pParse->hasNonstd = 1; }else if( c=='"' ){ opcode = JSONB_TEXT5; } @@ -205332,6 +206804,7 @@ json_parse_restart: return i+4; } /* fall-through into the default case that checks for NaN */ + /* no break */ deliberate_fall_through } default: { u32 k; @@ -205600,7 +207073,7 @@ static u32 jsonTranslateBlobToText( zIn = (const char*)&pParse->aBlob[i+n]; jsonAppendChar(pOut, '"'); while( sz2>0 ){ - for(k=0; k0 ){ jsonAppendRawNZ(pOut, zIn, k); if( k>=sz2 ){ @@ -205615,6 +207088,13 @@ static u32 jsonTranslateBlobToText( sz2--; continue; } + if( zIn[0]<=0x1f ){ + if( pOut->nUsed+7>pOut->nAlloc && jsonStringGrow(pOut,7) ) break; + jsonAppendControlChar(pOut, zIn[0]); + zIn++; + sz2--; + continue; + } assert( zIn[0]=='\\' ); assert( sz2>=1 ); if( sz2<2 ){ @@ -205717,6 +207197,112 @@ static u32 jsonTranslateBlobToText( return i+n+sz; } +/* Context for recursion of json_pretty() +*/ +typedef struct JsonPretty JsonPretty; +struct JsonPretty { + JsonParse *pParse; /* The BLOB being rendered */ + JsonString *pOut; /* Generate pretty output into this string */ + const char *zIndent; /* Use this text for indentation */ + u32 szIndent; /* Bytes in zIndent[] */ + u32 nIndent; /* Current level of indentation */ +}; + +/* Append indentation to the pretty JSON under construction */ +static void jsonPrettyIndent(JsonPretty *pPretty){ + u32 jj; + for(jj=0; jjnIndent; jj++){ + jsonAppendRaw(pPretty->pOut, pPretty->zIndent, pPretty->szIndent); + } +} + +/* +** 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. +** +** This is a variant of jsonTranslateBlobToText() that "pretty-prints" +** the output. Extra whitespace is inserted to make the JSON easier +** for humans to read. +** +** 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 jsonTranslateBlobToPrettyText( + JsonPretty *pPretty, /* Pretty-printing context */ + u32 i /* Start rendering at this index */ +){ + u32 sz, n, j, iEnd; + const JsonParse *pParse = pPretty->pParse; + JsonString *pOut = pPretty->pOut; + n = jsonbPayloadSize(pParse, i, &sz); + if( n==0 ){ + pOut->eErr |= JSTRING_MALFORMED; + return pParse->nBlob+1; + } + switch( pParse->aBlob[i] & 0x0f ){ + case JSONB_ARRAY: { + j = i+n; + iEnd = j+sz; + jsonAppendChar(pOut, '['); + if( jnIndent++; + while( pOut->eErr==0 ){ + jsonPrettyIndent(pPretty); + j = jsonTranslateBlobToPrettyText(pPretty, j); + if( j>=iEnd ) break; + jsonAppendRawNZ(pOut, ",\n", 2); + } + jsonAppendChar(pOut, '\n'); + pPretty->nIndent--; + jsonPrettyIndent(pPretty); + } + jsonAppendChar(pOut, ']'); + i = iEnd; + break; + } + case JSONB_OBJECT: { + j = i+n; + iEnd = j+sz; + jsonAppendChar(pOut, '{'); + if( jnIndent++; + while( pOut->eErr==0 ){ + jsonPrettyIndent(pPretty); + j = jsonTranslateBlobToText(pParse, j, pOut); + if( j>iEnd ){ + pOut->eErr |= JSTRING_MALFORMED; + break; + } + jsonAppendRawNZ(pOut, ": ", 2); + j = jsonTranslateBlobToPrettyText(pPretty, j); + if( j>=iEnd ) break; + jsonAppendRawNZ(pOut, ",\n", 2); + } + jsonAppendChar(pOut, '\n'); + pPretty->nIndent--; + jsonPrettyIndent(pPretty); + } + jsonAppendChar(pOut, '}'); + i = iEnd; + break; + } + default: { + i = jsonTranslateBlobToText(pParse, i, pOut); + break; + } + } + return i; +} + + /* Return true if the input pJson ** ** For performance reasons, this routine does not do a detailed check of the @@ -206967,11 +208553,12 @@ static void jsonParseFunc( if( p==0 ) return; if( argc==1 ){ jsonDebugPrintBlob(p, 0, p->nBlob, 0, &out); - sqlite3_result_text64(ctx, out.zText, out.nChar, SQLITE_DYNAMIC, SQLITE_UTF8); + sqlite3_result_text64(ctx,out.zText,out.nChar,SQLITE_TRANSIENT,SQLITE_UTF8); }else{ jsonShowParse(p); } jsonParseFree(p); + sqlite3_str_reset(&out); } #endif /* SQLITE_DEBUG */ @@ -207070,13 +208657,6 @@ static void jsonArrayLengthFunc( jsonParseFree(p); } -/* True if the string is all digits */ -static int jsonAllDigits(const char *z, int n){ - int i; - for(i=0; i $[NUMBER] // Not PG. Purely for convenience */ jsonStringInit(&jx, ctx); - if( jsonAllDigits(zPath, nPath) ){ + if( sqlite3_value_type(argv[i])==SQLITE_INTEGER ){ jsonAppendRawNZ(&jx, "[", 1); jsonAppendRaw(&jx, zPath, nPath); jsonAppendRawNZ(&jx, "]", 2); @@ -207635,6 +209215,40 @@ json_type_done: jsonParseFree(p); } +/* +** json_pretty(JSON) +** json_pretty(JSON, INDENT) +** +** Return text that is a pretty-printed rendering of the input JSON. +** If the argument is not valid JSON, return NULL. +** +** The INDENT argument is text that is used for indentation. If omitted, +** it defaults to four spaces (the same as PostgreSQL). +*/ +static void jsonPrettyFunc( + sqlite3_context *ctx, + int argc, + sqlite3_value **argv +){ + JsonString s; /* The output string */ + JsonPretty x; /* Pretty printing context */ + + memset(&x, 0, sizeof(x)); + x.pParse = jsonParseFuncArg(ctx, argv[0], 0); + if( x.pParse==0 ) return; + x.pOut = &s; + jsonStringInit(&s, ctx); + if( argc==1 || (x.zIndent = (const char*)sqlite3_value_text(argv[1]))==0 ){ + x.zIndent = " "; + x.szIndent = 4; + }else{ + x.szIndent = (u32)strlen(x.zIndent); + } + jsonTranslateBlobToPrettyText(&x, 0); + jsonReturnString(&s, 0, 0); + jsonParseFree(x.pParse); +} + /* ** json_valid(JSON) ** json_valid(JSON, FLAGS) @@ -208649,6 +210263,8 @@ SQLITE_PRIVATE void sqlite3RegisterJsonFunctions(void){ 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_pretty, 1,1,0, 0,0,0, jsonPrettyFunc), + JFUNCTION(json_pretty, 2,1,0, 0,0,0, jsonPrettyFunc), 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), @@ -210548,6 +212164,8 @@ static int deserializeGeometry(sqlite3_value *pValue, RtreeConstraint *pCons){ return SQLITE_OK; } +SQLITE_PRIVATE int sqlite3IntFloatCompare(i64,double); + /* ** Rtree virtual table module xFilter method. */ @@ -210577,7 +212195,8 @@ static int rtreeFilter( i64 iNode = 0; int eType = sqlite3_value_numeric_type(argv[0]); if( eType==SQLITE_INTEGER - || (eType==SQLITE_FLOAT && sqlite3_value_double(argv[0])==iRowid) + || (eType==SQLITE_FLOAT + && 0==sqlite3IntFloatCompare(iRowid,sqlite3_value_double(argv[0]))) ){ rc = findLeafNode(pRtree, iRowid, &pLeaf, &iNode); }else{ @@ -211932,6 +213551,7 @@ constraint: */ static int rtreeBeginTransaction(sqlite3_vtab *pVtab){ Rtree *pRtree = (Rtree *)pVtab; + assert( pRtree->inWrTrans==0 ); pRtree->inWrTrans = 1; return SQLITE_OK; } @@ -215486,7 +217106,7 @@ static void icuLoadCollation( UCollator *pUCollator; /* ICU library collation object */ int rc; /* Return code from sqlite3_create_collation_x() */ - assert(nArg==2); + assert(nArg==2 || nArg==3); (void)nArg; /* Unused parameter */ zLocale = (const char *)sqlite3_value_text(apArg[0]); zName = (const char *)sqlite3_value_text(apArg[1]); @@ -215501,7 +217121,39 @@ static void icuLoadCollation( return; } assert(p); - + if(nArg==3){ + const char *zOption = (const char*)sqlite3_value_text(apArg[2]); + static const struct { + const char *zName; + UColAttributeValue val; + } aStrength[] = { + { "PRIMARY", UCOL_PRIMARY }, + { "SECONDARY", UCOL_SECONDARY }, + { "TERTIARY", UCOL_TERTIARY }, + { "DEFAULT", UCOL_DEFAULT_STRENGTH }, + { "QUARTERNARY", UCOL_QUATERNARY }, + { "IDENTICAL", UCOL_IDENTICAL }, + }; + unsigned int i; + for(i=0; i=sizeof(aStrength)/sizeof(aStrength[0]) ){ + sqlite3_str *pStr = sqlite3_str_new(sqlite3_context_db_handle(p)); + sqlite3_str_appendf(pStr, + "unknown collation strength \"%s\" - should be one of:", + zOption); + for(i=0; ipTblIter, &p->zErrmsg); pIter->zTbl = 0; + pIter->zDataTbl = 0; }else{ pIter->zTbl = (const char*)sqlite3_column_text(pIter->pTblIter, 0); pIter->zDataTbl = (const char*)sqlite3_column_text(pIter->pTblIter,1); @@ -219454,7 +221109,7 @@ static i64 rbuShmChecksum(sqlite3rbu *p){ u32 volatile *ptr; p->rc = pDb->pMethods->xShmMap(pDb, 0, 32*1024, 0, (void volatile**)&ptr); if( p->rc==SQLITE_OK ){ - iRet = ((i64)ptr[10] << 32) + ptr[11]; + iRet = (i64)(((u64)ptr[10] << 32) + ptr[11]); } } return iRet; @@ -226925,14 +228580,14 @@ static int sessionChangesetNextOne( p->rc = sessionInputBuffer(&p->in, 2); if( p->rc!=SQLITE_OK ) return p->rc; + sessionDiscardData(&p->in); + p->in.iCurrent = p->in.iNext; + /* If the iterator is already at the end of the changeset, return DONE. */ if( p->in.iNext>=p->in.nData ){ return SQLITE_DONE; } - sessionDiscardData(&p->in); - p->in.iCurrent = p->in.iNext; - op = p->in.aData[p->in.iNext++]; while( op=='T' || op=='P' ){ if( pbNew ) *pbNew = 1; @@ -228667,6 +230322,7 @@ struct sqlite3_changegroup { int rc; /* Error code */ int bPatch; /* True to accumulate patchsets */ SessionTable *pList; /* List of tables in current patch */ + SessionBuffer rec; sqlite3 *db; /* Configured by changegroup_schema() */ char *zDb; /* Configured by changegroup_schema() */ @@ -228965,108 +230621,128 @@ static int sessionChangesetExtendRecord( } /* -** Add all changes in the changeset traversed by the iterator passed as -** the first argument to the changegroup hash tables. +** Locate or create a SessionTable object that may be used to add the +** change currently pointed to by iterator pIter to changegroup pGrp. +** If successful, set output variable (*ppTab) to point to the table +** object and return SQLITE_OK. Otherwise, if some error occurs, return +** an SQLite error code and leave (*ppTab) set to NULL. */ -static int sessionChangesetToHash( - sqlite3_changeset_iter *pIter, /* Iterator to read from */ - sqlite3_changegroup *pGrp, /* Changegroup object to add changeset to */ - int bRebase /* True if hash table is for rebasing */ +static int sessionChangesetFindTable( + sqlite3_changegroup *pGrp, + const char *zTab, + sqlite3_changeset_iter *pIter, + SessionTable **ppTab ){ - u8 *aRec; - int nRec; int rc = SQLITE_OK; SessionTable *pTab = 0; - SessionBuffer rec = {0, 0, 0}; + int nTab = (int)strlen(zTab); + u8 *abPK = 0; + int nCol = 0; - while( SQLITE_ROW==sessionChangesetNext(pIter, &aRec, &nRec, 0) ){ - const char *zNew; - int nCol; - int op; - int iHash; - int bIndirect; - SessionChange *pChange; - SessionChange *pExist = 0; - SessionChange **pp; + *ppTab = 0; + sqlite3changeset_pk(pIter, &abPK, &nCol); - /* 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 ){ - rc = SQLITE_ERROR; - break; + /* Search the list for an existing table */ + for(pTab = pGrp->pList; pTab; pTab=pTab->pNext){ + if( 0==sqlite3_strnicmp(pTab->zName, zTab, nTab+1) ) break; + } + + /* If one was not found above, create a new table now */ + if( !pTab ){ + SessionTable **ppNew; + + pTab = sqlite3_malloc64(sizeof(SessionTable) + nCol + nTab+1); + if( !pTab ){ + return SQLITE_NOMEM; } + memset(pTab, 0, sizeof(SessionTable)); + pTab->nCol = nCol; + pTab->abPK = (u8*)&pTab[1]; + memcpy(pTab->abPK, abPK, nCol); + pTab->zName = (char*)&pTab->abPK[nCol]; + memcpy(pTab->zName, zTab, nTab+1); - sqlite3changeset_op(pIter, &zNew, &nCol, &op, &bIndirect); - if( !pTab || sqlite3_stricmp(zNew, pTab->zName) ){ - /* Search the list for a matching table */ - int nNew = (int)strlen(zNew); - u8 *abPK; - - sqlite3changeset_pk(pIter, &abPK, 0); - for(pTab = pGrp->pList; pTab; pTab=pTab->pNext){ - if( 0==sqlite3_strnicmp(pTab->zName, zNew, nNew+1) ) break; - } - if( !pTab ){ - SessionTable **ppTab; - - pTab = sqlite3_malloc64(sizeof(SessionTable) + nCol + nNew+1); - if( !pTab ){ - rc = SQLITE_NOMEM; - break; - } - memset(pTab, 0, sizeof(SessionTable)); - pTab->nCol = nCol; - pTab->abPK = (u8*)&pTab[1]; - memcpy(pTab->abPK, abPK, nCol); - 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; - } - - if( !sessionChangesetCheckCompat(pTab, nCol, abPK) ){ - rc = SQLITE_SCHEMA; - break; + if( pGrp->db ){ + pTab->nCol = 0; + rc = sessionInitTable(0, pTab, pGrp->db, pGrp->zDb); + if( rc ){ + assert( pTab->azCol==0 ); + sqlite3_free(pTab); + return rc; } } - if( nColnCol ){ - assert( pGrp->db ); - rc = sessionChangesetExtendRecord(pGrp, pTab, nCol, op, aRec, nRec, &rec); - if( rc ) break; - aRec = rec.aBuf; - nRec = rec.nBuf; - } + /* 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(ppNew=&pGrp->pList; *ppNew; ppNew=&(*ppNew)->pNext); + *ppNew = pTab; + } - if( sessionGrowHash(0, pIter->bPatchset, pTab) ){ - rc = SQLITE_NOMEM; - break; - } + /* Check that the table is compatible. */ + if( !sessionChangesetCheckCompat(pTab, nCol, abPK) ){ + rc = SQLITE_SCHEMA; + } + + *ppTab = pTab; + return rc; +} + +/* +** Add the change currently indicated by iterator pIter to the hash table +** belonging to changegroup pGrp. +*/ +static int sessionOneChangeToHash( + sqlite3_changegroup *pGrp, + sqlite3_changeset_iter *pIter, + int bRebase +){ + int rc = SQLITE_OK; + int nCol = 0; + int op = 0; + int iHash = 0; + int bIndirect = 0; + SessionChange *pChange = 0; + SessionChange *pExist = 0; + SessionChange **pp = 0; + SessionTable *pTab = 0; + u8 *aRec = &pIter->in.aData[pIter->in.iCurrent + 2]; + int nRec = (pIter->in.iNext - pIter->in.iCurrent) - 2; + + /* 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 ){ + rc = SQLITE_ERROR; + } + + if( rc==SQLITE_OK ){ + const char *zTab = 0; + sqlite3changeset_op(pIter, &zTab, &nCol, &op, &bIndirect); + rc = sessionChangesetFindTable(pGrp, zTab, pIter, &pTab); + } + + if( rc==SQLITE_OK && nColnCol ){ + SessionBuffer *pBuf = &pGrp->rec; + rc = sessionChangesetExtendRecord(pGrp, pTab, nCol, op, aRec, nRec, pBuf); + aRec = pBuf->aBuf; + nRec = pBuf->nBuf; + assert( pGrp->db ); + } + + if( rc==SQLITE_OK && sessionGrowHash(0, pIter->bPatchset, pTab) ){ + rc = SQLITE_NOMEM; + } + + if( rc==SQLITE_OK ){ + /* Search for existing entry. If found, remove it from the hash table. + ** Code below may link it back in. */ iHash = sessionChangeHash( pTab, (pIter->bPatchset && op==SQLITE_DELETE), aRec, pTab->nChange ); - - /* Search for existing entry. If found, remove it from the hash table. - ** Code below may link it back in. - */ for(pp=&pTab->apChange[iHash]; *pp; pp=&(*pp)->pNext){ int bPkOnly1 = 0; int bPkOnly2 = 0; @@ -229081,19 +230757,41 @@ static int sessionChangesetToHash( break; } } + } + if( rc==SQLITE_OK ){ rc = sessionChangeMerge(pTab, bRebase, pIter->bPatchset, pExist, op, bIndirect, aRec, nRec, &pChange ); - if( rc ) break; - if( pChange ){ - pChange->pNext = pTab->apChange[iHash]; - pTab->apChange[iHash] = pChange; - pTab->nEntry++; - } + } + if( rc==SQLITE_OK && pChange ){ + pChange->pNext = pTab->apChange[iHash]; + pTab->apChange[iHash] = pChange; + pTab->nEntry++; + } + + if( rc==SQLITE_OK ) rc = pIter->rc; + return rc; +} + +/* +** Add all changes in the changeset traversed by the iterator passed as +** the first argument to the changegroup hash tables. +*/ +static int sessionChangesetToHash( + sqlite3_changeset_iter *pIter, /* Iterator to read from */ + sqlite3_changegroup *pGrp, /* Changegroup object to add changeset to */ + int bRebase /* True if hash table is for rebasing */ +){ + u8 *aRec; + int nRec; + int rc = SQLITE_OK; + + while( SQLITE_ROW==(sessionChangesetNext(pIter, &aRec, &nRec, 0)) ){ + rc = sessionOneChangeToHash(pGrp, pIter, bRebase); + if( rc!=SQLITE_OK ) break; } - sqlite3_free(rec.aBuf); if( rc==SQLITE_OK ) rc = pIter->rc; return rc; } @@ -229221,6 +230919,23 @@ SQLITE_API int sqlite3changegroup_add(sqlite3_changegroup *pGrp, int nData, void return rc; } +/* +** Add a single change to a changeset-group. +*/ +SQLITE_API int sqlite3changegroup_add_change( + sqlite3_changegroup *pGrp, + sqlite3_changeset_iter *pIter +){ + if( pIter->in.iCurrent==pIter->in.iNext + || pIter->rc!=SQLITE_OK + || pIter->bInvert + ){ + /* Iterator does not point to any valid entry or is an INVERT iterator. */ + return SQLITE_ERROR; + } + return sessionOneChangeToHash(pGrp, pIter, 0); +} + /* ** Obtain a buffer containing a changeset representing the concatenation ** of all changesets added to the group so far. @@ -229270,6 +230985,7 @@ SQLITE_API void sqlite3changegroup_delete(sqlite3_changegroup *pGrp){ if( pGrp ){ sqlite3_free(pGrp->zDb); sessionDeleteTable(0, pGrp->pList); + sqlite3_free(pGrp->rec.aBuf); sqlite3_free(pGrp); } } @@ -229671,6 +231387,7 @@ SQLITE_API int sqlite3rebaser_rebase_strm( SQLITE_API void sqlite3rebaser_delete(sqlite3_rebaser *p){ if( p ){ sessionDeleteTable(0, p->grp.pList); + sqlite3_free(p->grp.rec.aBuf); sqlite3_free(p); } } @@ -229768,8 +231485,8 @@ struct Fts5PhraseIter { ** EXTENSION API FUNCTIONS ** ** xUserData(pFts): -** Return a copy of the context pointer the extension function was -** registered with. +** Return a copy of the pUserData pointer passed to the xCreateFunction() +** API when the extension function was registered. ** ** xColumnTotalSize(pFts, iCol, pnToken): ** If parameter iCol is less than zero, set output variable *pnToken @@ -231365,6 +233082,9 @@ static void sqlite3Fts5UnicodeAscii(u8*, u8*); ** sqlite3Fts5ParserARG_STORE Code to store %extra_argument into fts5yypParser ** sqlite3Fts5ParserARG_FETCH Code to extract %extra_argument from fts5yypParser ** sqlite3Fts5ParserCTX_* As sqlite3Fts5ParserARG_ except for %extra_context +** fts5YYREALLOC Name of the realloc() function to use +** fts5YYFREE Name of the free() function to use +** fts5YYDYNSTACK True if stack space should be extended on heap ** fts5YYERRORSYMBOL is the code number of the error symbol. If not ** defined, then do no error processing. ** fts5YYNSTATE the combined number of states. @@ -231378,6 +233098,8 @@ static void sqlite3Fts5UnicodeAscii(u8*, u8*); ** fts5YY_NO_ACTION The fts5yy_action[] code for no-op ** fts5YY_MIN_REDUCE Minimum value for reduce actions ** fts5YY_MAX_REDUCE Maximum value for reduce actions +** fts5YY_MIN_DSTRCTR Minimum symbol value that has a destructor +** fts5YY_MAX_DSTRCTR Maximum symbol value that has a destructor */ #ifndef INTERFACE # define INTERFACE 1 @@ -231404,6 +233126,9 @@ typedef union { #define sqlite3Fts5ParserARG_PARAM ,pParse #define sqlite3Fts5ParserARG_FETCH Fts5Parse *pParse=fts5yypParser->pParse; #define sqlite3Fts5ParserARG_STORE fts5yypParser->pParse=pParse; +#define fts5YYREALLOC realloc +#define fts5YYFREE free +#define fts5YYDYNSTACK 0 #define sqlite3Fts5ParserCTX_SDECL #define sqlite3Fts5ParserCTX_PDECL #define sqlite3Fts5ParserCTX_PARAM @@ -231421,6 +233146,8 @@ typedef union { #define fts5YY_NO_ACTION 82 #define fts5YY_MIN_REDUCE 83 #define fts5YY_MAX_REDUCE 110 +#define fts5YY_MIN_DSTRCTR 16 +#define fts5YY_MAX_DSTRCTR 24 /************* End control #defines *******************************************/ #define fts5YY_NLOOKAHEAD ((int)(sizeof(fts5yy_lookahead)/sizeof(fts5yy_lookahead[0]))) @@ -231436,6 +233163,22 @@ typedef union { # define fts5yytestcase(X) #endif +/* Macro to determine if stack space has the ability to grow using +** heap memory. +*/ +#if fts5YYSTACKDEPTH<=0 || fts5YYDYNSTACK +# define fts5YYGROWABLESTACK 1 +#else +# define fts5YYGROWABLESTACK 0 +#endif + +/* Guarantee a minimum number of initial stack slots. +*/ +#if fts5YYSTACKDEPTH<=0 +# undef fts5YYSTACKDEPTH +# define fts5YYSTACKDEPTH 2 /* Need a minimum stack size */ +#endif + /* Next are the tables used to determine what action to take based on the ** current state and lookahead token. These tables are used to implement @@ -231596,14 +233339,9 @@ struct fts5yyParser { #endif sqlite3Fts5ParserARG_SDECL /* A place to hold %extra_argument */ sqlite3Fts5ParserCTX_SDECL /* A place to hold %extra_context */ -#if fts5YYSTACKDEPTH<=0 - int fts5yystksz; /* Current side of the stack */ - fts5yyStackEntry *fts5yystack; /* The parser's stack */ - fts5yyStackEntry fts5yystk0; /* First stack entry */ -#else - fts5yyStackEntry fts5yystack[fts5YYSTACKDEPTH]; /* The parser's stack */ - fts5yyStackEntry *fts5yystackEnd; /* Last entry in the stack */ -#endif + fts5yyStackEntry *fts5yystackEnd; /* Last entry in the stack */ + fts5yyStackEntry *fts5yystack; /* The parser stack */ + fts5yyStackEntry fts5yystk0[fts5YYSTACKDEPTH]; /* Initial stack space */ }; typedef struct fts5yyParser fts5yyParser; @@ -231710,37 +233448,45 @@ static const char *const fts5yyRuleName[] = { #endif /* NDEBUG */ -#if fts5YYSTACKDEPTH<=0 +#if fts5YYGROWABLESTACK /* ** Try to increase the size of the parser stack. Return the number ** of errors. Return 0 on success. */ static int fts5yyGrowStack(fts5yyParser *p){ + int oldSize = 1 + (int)(p->fts5yystackEnd - p->fts5yystack); int newSize; int idx; fts5yyStackEntry *pNew; - newSize = p->fts5yystksz*2 + 100; - idx = p->fts5yytos ? (int)(p->fts5yytos - p->fts5yystack) : 0; - if( p->fts5yystack==&p->fts5yystk0 ){ - pNew = malloc(newSize*sizeof(pNew[0])); - if( pNew ) pNew[0] = p->fts5yystk0; + newSize = oldSize*2 + 100; + idx = (int)(p->fts5yytos - p->fts5yystack); + if( p->fts5yystack==p->fts5yystk0 ){ + pNew = fts5YYREALLOC(0, newSize*sizeof(pNew[0])); + if( pNew==0 ) return 1; + memcpy(pNew, p->fts5yystack, oldSize*sizeof(pNew[0])); }else{ - pNew = realloc(p->fts5yystack, newSize*sizeof(pNew[0])); + pNew = fts5YYREALLOC(p->fts5yystack, newSize*sizeof(pNew[0])); + if( pNew==0 ) return 1; } - if( pNew ){ - p->fts5yystack = pNew; - p->fts5yytos = &p->fts5yystack[idx]; + p->fts5yystack = pNew; + p->fts5yytos = &p->fts5yystack[idx]; #ifndef NDEBUG - if( fts5yyTraceFILE ){ - fprintf(fts5yyTraceFILE,"%sStack grows from %d to %d entries.\n", - fts5yyTracePrompt, p->fts5yystksz, newSize); - } -#endif - p->fts5yystksz = newSize; + if( fts5yyTraceFILE ){ + fprintf(fts5yyTraceFILE,"%sStack grows from %d to %d entries.\n", + fts5yyTracePrompt, oldSize, newSize); } - return pNew==0; +#endif + p->fts5yystackEnd = &p->fts5yystack[newSize-1]; + return 0; } +#endif /* fts5YYGROWABLESTACK */ + +#if !fts5YYGROWABLESTACK +/* For builds that do no have a growable stack, fts5yyGrowStack always +** returns an error. +*/ +# define fts5yyGrowStack(X) 1 #endif /* Datatype of the argument to the memory allocated passed as the @@ -231760,24 +233506,14 @@ static void sqlite3Fts5ParserInit(void *fts5yypRawParser sqlite3Fts5ParserCTX_PD #ifdef fts5YYTRACKMAXSTACKDEPTH fts5yypParser->fts5yyhwm = 0; #endif -#if fts5YYSTACKDEPTH<=0 - fts5yypParser->fts5yytos = NULL; - fts5yypParser->fts5yystack = NULL; - fts5yypParser->fts5yystksz = 0; - if( fts5yyGrowStack(fts5yypParser) ){ - fts5yypParser->fts5yystack = &fts5yypParser->fts5yystk0; - fts5yypParser->fts5yystksz = 1; - } -#endif + fts5yypParser->fts5yystack = fts5yypParser->fts5yystk0; + fts5yypParser->fts5yystackEnd = &fts5yypParser->fts5yystack[fts5YYSTACKDEPTH-1]; #ifndef fts5YYNOERRORRECOVERY fts5yypParser->fts5yyerrcnt = -1; #endif fts5yypParser->fts5yytos = fts5yypParser->fts5yystack; fts5yypParser->fts5yystack[0].stateno = 0; fts5yypParser->fts5yystack[0].major = 0; -#if fts5YYSTACKDEPTH>0 - fts5yypParser->fts5yystackEnd = &fts5yypParser->fts5yystack[fts5YYSTACKDEPTH-1]; -#endif } #ifndef sqlite3Fts5Parser_ENGINEALWAYSONSTACK @@ -231891,9 +233627,26 @@ static void fts5yy_pop_parser_stack(fts5yyParser *pParser){ */ static void sqlite3Fts5ParserFinalize(void *p){ fts5yyParser *pParser = (fts5yyParser*)p; - while( pParser->fts5yytos>pParser->fts5yystack ) fts5yy_pop_parser_stack(pParser); -#if fts5YYSTACKDEPTH<=0 - if( pParser->fts5yystack!=&pParser->fts5yystk0 ) free(pParser->fts5yystack); + + /* In-lined version of calling fts5yy_pop_parser_stack() for each + ** element left in the stack */ + fts5yyStackEntry *fts5yytos = pParser->fts5yytos; + while( fts5yytos>pParser->fts5yystack ){ +#ifndef NDEBUG + if( fts5yyTraceFILE ){ + fprintf(fts5yyTraceFILE,"%sPopping %s\n", + fts5yyTracePrompt, + fts5yyTokenName[fts5yytos->major]); + } +#endif + if( fts5yytos->major>=fts5YY_MIN_DSTRCTR ){ + fts5yy_destructor(pParser, fts5yytos->major, &fts5yytos->minor); + } + fts5yytos--; + } + +#if fts5YYGROWABLESTACK + if( pParser->fts5yystack!=pParser->fts5yystk0 ) fts5YYFREE(pParser->fts5yystack); #endif } @@ -232120,25 +233873,19 @@ static void fts5yy_shift( assert( fts5yypParser->fts5yyhwm == (int)(fts5yypParser->fts5yytos - fts5yypParser->fts5yystack) ); } #endif -#if fts5YYSTACKDEPTH>0 - if( fts5yypParser->fts5yytos>fts5yypParser->fts5yystackEnd ){ - fts5yypParser->fts5yytos--; - fts5yyStackOverflow(fts5yypParser); - return; - } -#else - if( fts5yypParser->fts5yytos>=&fts5yypParser->fts5yystack[fts5yypParser->fts5yystksz] ){ + fts5yytos = fts5yypParser->fts5yytos; + if( fts5yytos>fts5yypParser->fts5yystackEnd ){ if( fts5yyGrowStack(fts5yypParser) ){ fts5yypParser->fts5yytos--; fts5yyStackOverflow(fts5yypParser); return; } + fts5yytos = fts5yypParser->fts5yytos; + assert( fts5yytos <= fts5yypParser->fts5yystackEnd ); } -#endif if( fts5yyNewState > fts5YY_MAX_SHIFT ){ fts5yyNewState += fts5YY_MIN_REDUCE - fts5YY_MIN_SHIFTREDUCE; } - fts5yytos = fts5yypParser->fts5yytos; fts5yytos->stateno = fts5yyNewState; fts5yytos->major = fts5yyMajor; fts5yytos->minor.fts5yy0 = fts5yyMinor; @@ -232575,19 +234322,12 @@ static void sqlite3Fts5Parser( (int)(fts5yypParser->fts5yytos - fts5yypParser->fts5yystack)); } #endif -#if fts5YYSTACKDEPTH>0 if( fts5yypParser->fts5yytos>=fts5yypParser->fts5yystackEnd ){ - fts5yyStackOverflow(fts5yypParser); - break; - } -#else - if( fts5yypParser->fts5yytos>=&fts5yypParser->fts5yystack[fts5yypParser->fts5yystksz-1] ){ if( fts5yyGrowStack(fts5yypParser) ){ fts5yyStackOverflow(fts5yypParser); break; } } -#endif } fts5yyact = fts5yy_reduce(fts5yypParser,fts5yyruleno,fts5yymajor,fts5yyminor sqlite3Fts5ParserCTX_PARAM); }else if( fts5yyact <= fts5YY_MAX_SHIFTREDUCE ){ @@ -250799,7 +252539,7 @@ static void fts5SourceIdFunc( ){ assert( nArg==0 ); UNUSED_PARAM2(nArg, apUnused); - sqlite3_result_text(pCtx, "fts5: 2024-04-15 13:34:05 8653b758870e6ef0c98d46b3ace27849054af85da891eb121e9aaa537f1e8355", -1, SQLITE_TRANSIENT); + sqlite3_result_text(pCtx, "fts5: 2024-05-23 13:25:27 96c92aba00c8375bc32fafcdf12429c58bd8aabfcadab6683e35bbb9cdebf19e", -1, SQLITE_TRANSIENT); } /* @@ -250838,6 +252578,7 @@ static int fts5IntegrityMethod( if( (rc&0xff)==SQLITE_CORRUPT ){ *pzErr = sqlite3_mprintf("malformed inverted index for FTS5 table %s.%s", zSchema, zTabname); + rc = (*pzErr) ? SQLITE_OK : SQLITE_NOMEM; }else if( rc!=SQLITE_OK ){ *pzErr = sqlite3_mprintf("unable to validate the inverted index for" " FTS5 table %s.%s: %s", @@ -250845,7 +252586,7 @@ static int fts5IntegrityMethod( } sqlite3Fts5IndexCloseReader(pTab->p.pIndex); - return SQLITE_OK; + return rc; } static int fts5Init(sqlite3 *db){ diff --git a/src/libs/3rdparty/sqlite/sqlite3.h b/src/libs/3rdparty/sqlite/sqlite3.h index 2618b37a7b8..57df8dcf202 100644 --- a/src/libs/3rdparty/sqlite/sqlite3.h +++ b/src/libs/3rdparty/sqlite/sqlite3.h @@ -146,9 +146,9 @@ extern "C" { ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ -#define SQLITE_VERSION "3.45.3" -#define SQLITE_VERSION_NUMBER 3045003 -#define SQLITE_SOURCE_ID "2024-04-15 13:34:05 8653b758870e6ef0c98d46b3ace27849054af85da891eb121e9aaa537f1e8355" +#define SQLITE_VERSION "3.46.0" +#define SQLITE_VERSION_NUMBER 3046000 +#define SQLITE_SOURCE_ID "2024-05-23 13:25:27 96c92aba00c8375bc32fafcdf12429c58bd8aabfcadab6683e35bbb9cdebf19e" /* ** CAPI3REF: Run-Time Library Version Numbers @@ -764,11 +764,11 @@ struct sqlite3_file { ** ** xLock() upgrades the database file lock. In other words, xLock() moves the ** database file lock in the direction NONE toward EXCLUSIVE. The argument to -** xLock() is always on of SHARED, RESERVED, PENDING, or EXCLUSIVE, never +** xLock() is always one of SHARED, RESERVED, PENDING, or EXCLUSIVE, never ** SQLITE_LOCK_NONE. If the database file lock is already at or above the ** requested lock, then the call to xLock() is a no-op. ** xUnlock() downgrades the database file lock to either SHARED or NONE. -* If the lock is already at or below the requested lock state, then the call +** If the lock is already at or below the requested lock state, then the call ** to xUnlock() is a no-op. ** The xCheckReservedLock() method checks whether any database connection, ** either in this process or in some other process, is holding a RESERVED, @@ -3305,8 +3305,8 @@ SQLITE_API int sqlite3_set_authorizer( #define SQLITE_RECURSIVE 33 /* NULL NULL */ /* -** CAPI3REF: Tracing And Profiling Functions -** METHOD: sqlite3 +** CAPI3REF: Deprecated Tracing And Profiling Functions +** DEPRECATED ** ** These routines are deprecated. Use the [sqlite3_trace_v2()] interface ** instead of the routines described here. @@ -6887,6 +6887,12 @@ SQLITE_API int sqlite3_autovacuum_pages( ** The exceptions defined in this paragraph might change in a future ** release of SQLite. ** +** Whether the update hook is invoked before or after the +** corresponding change is currently unspecified and may differ +** depending on the type of change. Do not rely on the order of the +** hook call with regards to the final result of the operation which +** triggers the hook. +** ** The update hook implementation must not do anything that will modify ** the database connection that invoked the update hook. Any actions ** to modify the database connection must be deferred until after the @@ -8357,7 +8363,7 @@ SQLITE_API int sqlite3_test_control(int op, ...); ** The sqlite3_keyword_count() interface returns the number of distinct ** keywords understood by SQLite. ** -** The sqlite3_keyword_name(N,Z,L) interface finds the N-th keyword and +** The sqlite3_keyword_name(N,Z,L) interface finds the 0-based N-th keyword and ** makes *Z point to that keyword expressed as UTF8 and writes the number ** of bytes in the keyword into *L. The string that *Z points to is not ** zero-terminated. The sqlite3_keyword_name(N,Z,L) routine returns @@ -9936,24 +9942,45 @@ SQLITE_API const char *sqlite3_vtab_collation(sqlite3_index_info*,int); **

  • ** ^(If the sqlite3_vtab_distinct() interface returns 2, that means ** that the query planner does not need the rows returned in any particular -** order, as long as rows with the same values in all "aOrderBy" columns -** are adjacent.)^ ^(Furthermore, only a single row for each particular -** combination of values in the columns identified by the "aOrderBy" field -** needs to be returned.)^ ^It is always ok for two or more rows with the same -** values in all "aOrderBy" columns to be returned, as long as all such rows -** are adjacent. ^The virtual table may, if it chooses, omit extra rows -** that have the same value for all columns identified by "aOrderBy". -** ^However omitting the extra rows is optional. +** order, as long as rows with the same values in all columns identified +** by "aOrderBy" are adjacent.)^ ^(Furthermore, when two or more rows +** contain the same values for all columns identified by "colUsed", all but +** one such row may optionally be omitted from the result.)^ +** The virtual table is not required to omit rows that are duplicates +** over the "colUsed" columns, but if the virtual table can do that without +** too much extra effort, it could potentially help the query to run faster. ** This mode is used for a DISTINCT query. **

  • -** ^(If the sqlite3_vtab_distinct() interface returns 3, that means -** that the query planner needs only distinct rows but it does need the -** rows to be sorted.)^ ^The virtual table implementation is free to omit -** rows that are identical in all aOrderBy columns, if it wants to, but -** it is not required to omit any rows. This mode is used for queries +** ^(If the sqlite3_vtab_distinct() interface returns 3, that means the +** virtual table must return rows in the order defined by "aOrderBy" as +** if the sqlite3_vtab_distinct() interface had returned 0. However if +** two or more rows in the result have the same values for all columns +** identified by "colUsed", then all but one such row may optionally be +** omitted.)^ Like when the return value is 2, the virtual table +** is not required to omit rows that are duplicates over the "colUsed" +** columns, but if the virtual table can do that without +** too much extra effort, it could potentially help the query to run faster. +** This mode is used for queries ** that have both DISTINCT and ORDER BY clauses. ** ** +**

    The following table summarizes the conditions under which the +** virtual table is allowed to set the "orderByConsumed" flag based on +** the value returned by sqlite3_vtab_distinct(). This table is a +** restatement of the previous four paragraphs: +** +** +** +**
    sqlite3_vtab_distinct() return value +** Rows are returned in aOrderBy order +** Rows with the same value in all aOrderBy columns are adjacent +** Duplicates over all colUsed columns may be omitted +**
    0yesyesno +**
    1noyesno +**
    2noyesyes +**
    3yesyesyes +**
    +** ** ^For the purposes of comparing virtual table output values to see if the ** values are same value for sorting purposes, two NULL values are considered ** to be the same. In other words, the comparison operator is "IS" @@ -11998,6 +12025,30 @@ SQLITE_API int sqlite3changegroup_schema(sqlite3_changegroup*, sqlite3*, const c */ SQLITE_API int sqlite3changegroup_add(sqlite3_changegroup*, int nData, void *pData); +/* +** CAPI3REF: Add A Single Change To A Changegroup +** METHOD: sqlite3_changegroup +** +** This function adds the single change currently indicated by the iterator +** passed as the second argument to the changegroup object. The rules for +** adding the change are just as described for [sqlite3changegroup_add()]. +** +** If the change is successfully added to the changegroup, SQLITE_OK is +** returned. Otherwise, an SQLite error code is returned. +** +** The iterator must point to a valid entry when this function is called. +** If it does not, SQLITE_ERROR is returned and no change is added to the +** changegroup. Additionally, the iterator must not have been opened with +** the SQLITE_CHANGESETAPPLY_INVERT flag. In this case SQLITE_ERROR is also +** returned. +*/ +SQLITE_API int sqlite3changegroup_add_change( + sqlite3_changegroup*, + sqlite3_changeset_iter* +); + + + /* ** CAPI3REF: Obtain A Composite Changeset From A Changegroup ** METHOD: sqlite3_changegroup @@ -12802,8 +12853,8 @@ struct Fts5PhraseIter { ** EXTENSION API FUNCTIONS ** ** xUserData(pFts): -** Return a copy of the context pointer the extension function was -** registered with. +** Return a copy of the pUserData pointer passed to the xCreateFunction() +** API when the extension function was registered. ** ** xColumnTotalSize(pFts, iCol, pnToken): ** If parameter iCol is less than zero, set output variable *pnToken diff --git a/src/libs/nanotrace/nanotracehr.h b/src/libs/nanotrace/nanotracehr.h index 810e4e3c5d7..15f8109fe91 100644 --- a/src/libs/nanotrace/nanotracehr.h +++ b/src/libs/nanotrace/nanotracehr.h @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -144,17 +145,10 @@ void convertToString(String &string, Number number) string.append(number); } -template -constexpr std::underlying_type_t to_underlying(Enumeration enumeration) noexcept -{ - static_assert(std::is_enum_v, "to_underlying expect an enumeration"); - return static_cast>(enumeration); -} - template, bool> = true> void convertToString(String &string, Enumeration enumeration) { - string.append(to_underlying(enumeration)); + string.append(Utils::to_underlying(enumeration)); } template diff --git a/src/libs/qmlpuppetcommunication/commands/puppettocreatorcommand.h b/src/libs/qmlpuppetcommunication/commands/puppettocreatorcommand.h index 1e8d82e632b..b0012f3eb24 100644 --- a/src/libs/qmlpuppetcommunication/commands/puppettocreatorcommand.h +++ b/src/libs/qmlpuppetcommunication/commands/puppettocreatorcommand.h @@ -18,6 +18,7 @@ public: ActiveSceneChanged, ActiveSplitChanged, RenderModelNodePreviewImage, + Import3DPreviewImage, Import3DSupport, NodeAtPos, BakeLightsProgress, diff --git a/src/libs/qmlpuppetcommunication/commands/view3dactioncommand.cpp b/src/libs/qmlpuppetcommunication/commands/view3dactioncommand.cpp index eac0961393a..a522499ffa7 100644 --- a/src/libs/qmlpuppetcommunication/commands/view3dactioncommand.cpp +++ b/src/libs/qmlpuppetcommunication/commands/view3dactioncommand.cpp @@ -3,6 +3,8 @@ #include "view3dactioncommand.h" +#include + #include #include @@ -64,16 +66,9 @@ QDebug operator<<(QDebug debug, const View3DActionCommand &command) << command.m_value << ")\n"; } -template -constexpr std::underlying_type_t to_underlying(Enumeration enumeration) noexcept -{ - static_assert(std::is_enum_v, "to_underlying expect an enumeration"); - return static_cast>(enumeration); -} - QDebug operator<<(QDebug debug, View3DActionType type) { - return debug.nospace() << to_underlying(type); + return debug.nospace() << Utils::to_underlying(type); } } // namespace QmlDesigner diff --git a/src/libs/qmlpuppetcommunication/interfaces/nodeinstanceglobal.h b/src/libs/qmlpuppetcommunication/interfaces/nodeinstanceglobal.h index a3012c99f53..d248e5e6fd1 100644 --- a/src/libs/qmlpuppetcommunication/interfaces/nodeinstanceglobal.h +++ b/src/libs/qmlpuppetcommunication/interfaces/nodeinstanceglobal.h @@ -36,6 +36,7 @@ enum class View3DActionType { OrientationToggle, EditLightToggle, ShowGrid, + ShowLookAt, ShowSelectionBox, ShowIconGizmo, ShowCameraFrustum, @@ -54,7 +55,10 @@ enum class View3DActionType { FlyModeToggle, EditCameraRotation, EditCameraMove, - EditCameraStopAllMoves + EditCameraStopAllMoves, + SetLastSceneEnvData, + Import3dUpdatePreviewImage, + Import3dRotatePreviewModel }; constexpr bool isNanotraceEnabled() diff --git a/src/libs/qmlpuppetcommunication/types/enumeration.h b/src/libs/qmlpuppetcommunication/types/enumeration.h index 57bfe6c0ef4..5fe2294621e 100644 --- a/src/libs/qmlpuppetcommunication/types/enumeration.h +++ b/src/libs/qmlpuppetcommunication/types/enumeration.h @@ -43,17 +43,20 @@ public: EnumerationNameView scope() const { - auto found = std::find(m_enumerationName.begin(), m_enumerationName.end(), '.'); - return {m_enumerationName.begin(), found}; + auto found = std::find(m_enumerationName.rbegin(), m_enumerationName.rend(), '.'); + if (found != m_enumerationName.rend()) + return {m_enumerationName.begin(), std::prev(found.base())}; + + return {m_enumerationName.end(), m_enumerationName.end()}; } EnumerationNameView toScope() const { return scope().toByteArray(); } EnumerationNameView name() const { - auto found = std::find(m_enumerationName.begin(), m_enumerationName.end(), '.'); - if (found != m_enumerationName.end()) - return {std::next(found), m_enumerationName.end()}; + auto found = std::find(m_enumerationName.rbegin(), m_enumerationName.rend(), '.'); + if (found != m_enumerationName.rend()) + return {found.base(), m_enumerationName.end()}; return {m_enumerationName.end(), m_enumerationName.end()}; } diff --git a/src/libs/sqlite/sqlitebasestatement.h b/src/libs/sqlite/sqlitebasestatement.h index 3710021ff53..64acba0a27a 100644 --- a/src/libs/sqlite/sqlitebasestatement.h +++ b/src/libs/sqlite/sqlitebasestatement.h @@ -36,13 +36,6 @@ class DatabaseBackend; enum class Type : char { Invalid, Integer, Float, Text, Blob, Null }; -template -constexpr static std::underlying_type_t to_underlying(Enumeration enumeration) noexcept -{ - static_assert(std::is_enum_v, "to_underlying expect an enumeration"); - return static_cast>(enumeration); -} - class SQLITE_EXPORT BaseStatement { public: @@ -87,7 +80,7 @@ public: template> void bind(int index, Type id) { - if (id) + if (!id.isNull()) bind(index, id.internalId()); else bindNull(index); @@ -96,7 +89,7 @@ public: template, bool> = true> void bind(int index, Enumeration enumeration) { - bind(index, to_underlying(enumeration)); + bind(index, Utils::to_underlying(enumeration)); } void bind(int index, uint value) { bind(index, static_cast(value)); } diff --git a/src/libs/sqlite/sqliteids.h b/src/libs/sqlite/sqliteids.h index 1ffd546d9f2..abbb13d7c16 100644 --- a/src/libs/sqlite/sqliteids.h +++ b/src/libs/sqlite/sqliteids.h @@ -4,6 +4,7 @@ #pragma once #include +#include #include #include @@ -27,7 +28,15 @@ public: return id; } - constexpr friend bool compareInvalidAreTrue(BasicId first, BasicId second) + template + static constexpr BasicId createSpecialState(Enumeration specialState) + { + BasicId id; + id.id = ::Utils::to_underlying(specialState); + return id; + } + + friend constexpr bool compareInvalidAreTrue(BasicId first, BasicId second) { return first.id == second.id; } @@ -57,6 +66,14 @@ public: constexpr bool isValid() const { return id > 0; } + constexpr bool isNull() const { return id == 0; } + + template + constexpr bool hasSpecialState(Enumeration specialState) const + { + return id == ::Utils::to_underlying(specialState); + } + explicit operator bool() const { return isValid(); } explicit operator std::size_t() const { return static_cast(id); } @@ -68,13 +85,13 @@ public: template friend void convertToString(String &string, BasicId id) { - if (id.isValid()) - NanotraceHR::convertToString(string, id.internalId()); + if (id.isNull()) + NanotraceHR::convertToString(string, "invalid null"); else - NanotraceHR::convertToString(string, "invalid"); + NanotraceHR::convertToString(string, id.internalId()); } -private: +protected: InternalIntegerType id = 0; }; diff --git a/src/libs/utils/CMakeLists.txt b/src/libs/utils/CMakeLists.txt index f5cc2cde80e..ca5e88b66c4 100644 --- a/src/libs/utils/CMakeLists.txt +++ b/src/libs/utils/CMakeLists.txt @@ -199,6 +199,7 @@ add_qtc_library(Utils utilstr.h utilsicons.cpp utilsicons.h utiltypes.h + utility.h variablechooser.cpp variablechooser.h winutils.cpp winutils.h wizard.cpp wizard.h diff --git a/src/libs/utils/utility.h b/src/libs/utils/utility.h new file mode 100644 index 00000000000..27de88d3398 --- /dev/null +++ b/src/libs/utils/utility.h @@ -0,0 +1,14 @@ +// 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 + +namespace Utils { + +template +[[nodiscard]] constexpr std::underlying_type_t to_underlying(Enumeration enumeration) noexcept +{ + return static_cast>(enumeration); +} + +} // namespace Utils diff --git a/src/plugins/effectcomposer/compositionnode.cpp b/src/plugins/effectcomposer/compositionnode.cpp index d939e2283af..a69cd00e075 100644 --- a/src/plugins/effectcomposer/compositionnode.cpp +++ b/src/plugins/effectcomposer/compositionnode.cpp @@ -128,7 +128,7 @@ void CompositionNode::parse(const QString &effectName, const QString &qenPath, c // parse properties QJsonArray jsonProps = json.value("properties").toArray(); - for (const auto /*QJsonValueRef*/ &prop : jsonProps) { + for (const QJsonValueConstRef &prop : jsonProps) { const auto uniform = new Uniform(effectName, prop.toObject(), qenPath); m_unifomrsModel.addUniform(uniform); m_uniforms.append(uniform); diff --git a/src/plugins/effectcomposer/effectcomposermodel.cpp b/src/plugins/effectcomposer/effectcomposermodel.cpp index a983072334b..43efefe51d6 100644 --- a/src/plugins/effectcomposer/effectcomposermodel.cpp +++ b/src/plugins/effectcomposer/effectcomposermodel.cpp @@ -21,6 +21,7 @@ #include #include +#include #include namespace EffectComposer { @@ -48,6 +49,7 @@ static bool writeToFile(const QByteArray &buf, const QString &filename, FileType EffectComposerModel::EffectComposerModel(QObject *parent) : QAbstractListModel{parent} + , m_shaderDir(QDir::tempPath() + "/qds_ec_XXXXXX") { m_rebakeTimer.setSingleShot(true); connect(&m_rebakeTimer, &QTimer::timeout, this, &EffectComposerModel::bakeShaders); @@ -516,11 +518,9 @@ QJsonObject nodeToJson(const CompositionNode &node) uniformObject.insert("type", type); - if (uniform->type() == Uniform::Type::Define || uniform->type() == Uniform::Type::Channel) { - QString controlType = Uniform::stringFromType(uniform->controlType()); - if (controlType != type) - uniformObject.insert("controlType", controlType); - } + QString controlType = Uniform::stringFromType(uniform->controlType()); + if (controlType != type) + uniformObject.insert("controlType", controlType); if (!uniform->displayName().isEmpty()) uniformObject.insert("displayName", QString(uniform->displayName())); @@ -542,7 +542,8 @@ QJsonObject nodeToJson(const CompositionNode &node) if (!uniform->description().isEmpty()) uniformObject.insert("description", uniform->description()); if (uniform->type() == Uniform::Type::Float - || uniform->type() == Uniform::Type::Int + || (uniform->type() == Uniform::Type::Int + && uniform->controlType() != Uniform::Type::Channel) || uniform->type() == Uniform::Type::Vec2 || uniform->type() == Uniform::Type::Vec3 || uniform->type() == Uniform::Type::Vec4 @@ -1713,37 +1714,33 @@ void EffectComposerModel::updateCustomUniforms() m_exportedEffectPropertiesString = exportedEffectPropertiesString; } -void EffectComposerModel::createFiles() +void EffectComposerModel::initShaderDir() { - if (QFileInfo::exists(m_vertexShaderFilename)) - QFile(m_vertexShaderFilename).remove(); - if (QFileInfo::exists(m_fragmentShaderFilename)) - QFile(m_fragmentShaderFilename).remove(); - if (QFileInfo::exists(m_vertexShaderPreviewFilename)) - QFile(m_vertexShaderPreviewFilename).remove(); - if (QFileInfo::exists(m_fragmentShaderPreviewFilename)) - QFile(m_fragmentShaderPreviewFilename).remove(); + static int count = 0; + static const QString fileNameTemplate = "%1_%2.%3"; + const QString countStr = QString::number(count); - auto vertexShaderFile = QTemporaryFile(QDir::tempPath() + "/dsem_XXXXXX.vert.qsb"); - auto fragmentShaderFile = QTemporaryFile(QDir::tempPath() + "/dsem_XXXXXX.frag.qsb"); - auto vertexShaderPreviewFile = QTemporaryFile(QDir::tempPath() + "/dsem_prev_XXXXXX.vert.qsb"); - auto fragmentShaderPreviewFile = QTemporaryFile(QDir::tempPath() + "/dsem_prev_XXXXXX.frag.qsb"); + auto resetFile = [&countStr, this](QString &fileName, const QString prefix, const QString suffix) { + // qsb generation is done in separate process, so it is not guaranteed all of the old files + // get deleted here, as they may not exist yet. Any dangling files will be deleted at + // application shutdown, when the temporary directory is destroyed. + if (!fileName.isEmpty()) { + QFile file(fileName); + if (file.exists()) + file.remove(); + } - m_vertexSourceFile.setFileTemplate(QDir::tempPath() + "/dsem_XXXXXX.vert"); - m_fragmentSourceFile.setFileTemplate(QDir::tempPath() + "/dsem_XXXXXX.frag"); + fileName = m_shaderDir.filePath(fileNameTemplate.arg(prefix, countStr, suffix)); + }; - if (!m_vertexSourceFile.open() || !m_fragmentSourceFile.open() - || !vertexShaderFile.open() || !fragmentShaderFile.open() - || !vertexShaderPreviewFile.open() || !fragmentShaderPreviewFile.open()) { - qWarning() << "Unable to open temporary files"; - } else { - m_vertexSourceFilename = m_vertexSourceFile.fileName(); - m_fragmentSourceFilename = m_fragmentSourceFile.fileName(); - m_vertexShaderFilename = vertexShaderFile.fileName(); - m_fragmentShaderFilename = fragmentShaderFile.fileName(); - m_vertexShaderPreviewFilename = vertexShaderPreviewFile.fileName(); - m_fragmentShaderPreviewFilename = fragmentShaderPreviewFile.fileName(); - } + resetFile(m_vertexSourceFilename, "source", "vert"); + resetFile(m_fragmentSourceFilename, "source", "frag"); + resetFile(m_vertexShaderFilename, "compiled", "vert.qsb"); + resetFile(m_fragmentShaderFilename, "compiled", "frag.qsb"); + resetFile(m_vertexShaderPreviewFilename, "compiled_prev", "vert.qsb"); + resetFile(m_fragmentShaderPreviewFilename, "compiled_prev", "frag.qsb"); + + ++count; } void EffectComposerModel::bakeShaders() @@ -1756,7 +1753,7 @@ void EffectComposerModel::bakeShaders() return; } - createFiles(); + initShaderDir(); resetEffectError(ErrorPreprocessor); if (m_vertexShader == generateVertexShader() && m_fragmentShader == generateFragmentShader()) { @@ -1775,11 +1772,11 @@ void EffectComposerModel::bakeShaders() setVertexShader(generateVertexShader()); QString vs = m_vertexShader; - writeToFile(vs.toUtf8(), m_vertexSourceFile.fileName(), FileType::Text); + writeToFile(vs.toUtf8(), m_vertexSourceFilename, FileType::Text); setFragmentShader(generateFragmentShader()); QString fs = m_fragmentShader; - writeToFile(fs.toUtf8(), m_fragmentSourceFile.fileName(), FileType::Text); + writeToFile(fs.toUtf8(), m_fragmentSourceFilename, FileType::Text); QtSupport::QtVersion *qtVer = QtSupport::QtKitAspect::qtVersion(target->kit()); if (!qtVer) { diff --git a/src/plugins/effectcomposer/effectcomposermodel.h b/src/plugins/effectcomposer/effectcomposermodel.h index 14ef09e8a97..b377ae06180 100644 --- a/src/plugins/effectcomposer/effectcomposermodel.h +++ b/src/plugins/effectcomposer/effectcomposermodel.h @@ -12,7 +12,7 @@ #include #include #include -#include +#include #include namespace ProjectExplorer { @@ -176,7 +176,7 @@ private: QString getQmlEffectString(); void updateCustomUniforms(); - void createFiles(); + void initShaderDir(); void bakeShaders(); void saveResources(const QString &name); @@ -205,8 +205,7 @@ private: QStringList m_defaultRootVertexShader; QStringList m_defaultRootFragmentShader; // Temp files to store shaders sources and binary data - QTemporaryFile m_fragmentSourceFile; - QTemporaryFile m_vertexSourceFile; + QTemporaryDir m_shaderDir; QString m_fragmentSourceFilename; QString m_vertexSourceFilename; QString m_fragmentShaderFilename; diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 0b0075915f2..c29017523d9 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -27,6 +27,10 @@ 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} "") +env_with_default("QTC_ENABLE_METAINFO_TRACING" ENV_QTC_ENABLE_METAINFO_TRACING OFF) +option(ENABLE_METAINFO_TRACING "Enable meta info tracing" ${ENV_QTC_ENABLE_METAINFO_TRACING}) +add_feature_info("Meta info tracing" ${ENABLE_METAINFO_TRACING} "") + add_qtc_library(QmlDesignerUtils STATIC DEPENDS Qt::Gui Utils Qt::QmlPrivate @@ -93,14 +97,13 @@ add_qtc_library(QmlDesignerCore STATIC ${CMAKE_CURRENT_LIST_DIR}/designercore/include SOURCES_PREFIX ${CMAKE_CURRENT_LIST_DIR}/designercore SOURCES - rewritertransaction.cpp - rewritertransaction.h - generatedcomponentutils.cpp - generatedcomponentutils.h + rewritertransaction.cpp rewritertransaction.h + generatedcomponentutils.cpp generatedcomponentutils.h + uniquename.cpp uniquename.h ) extend_qtc_library(QmlDesignerCore - CONDITION ENABLE_PROJECT_STORAGE_TRACING OR ENABLE_IMAGE_CACHE_TRACING OR ENABLE_MODEL_TRACING + CONDITION ENABLE_PROJECT_STORAGE_TRACING OR ENABLE_IMAGE_CACHE_TRACING OR ENABLE_MODEL_TRACING OR ENABLE_METAINFO_TRACING PUBLIC_DEPENDS Nanotrace PUBLIC_DEFINES ENABLE_QMLDESIGNER_TRACING @@ -108,6 +111,7 @@ extend_qtc_library(QmlDesignerCore $<$:ENABLE_PROJECT_STORAGE_TRACING> $<$:ENABLE_IMAGE_CACHE_TRACING> $<$:ENABLE_MODEL_TRACING> + $<$:ENABLE_METAINFO_TRACING> ) extend_qtc_library(QmlDesignerCore @@ -463,6 +467,8 @@ extend_qtc_library(QmlDesignerCore projectstoragetypes.h projectstorageupdater.cpp projectstorageupdater.h projectstorage.cpp projectstorage.h + projectstorageerrornotifierinterface.h + projectstorageerrornotifier.cpp projectstorageerrornotifier.h sourcepath.h sourcepathcache.h sourcepathcacheinterface.h @@ -498,7 +504,6 @@ add_qtc_plugin(QmlDesigner INCLUDES ${CMAKE_CURRENT_LIST_DIR}/components ${CMAKE_CURRENT_LIST_DIR}/components/assetslibrary - ${CMAKE_CURRENT_LIST_DIR}/components/collectioneditor ${CMAKE_CURRENT_LIST_DIR}/components/debugview ${CMAKE_CURRENT_LIST_DIR}/components/edit3d ${CMAKE_CURRENT_LIST_DIR}/components/formeditor @@ -739,6 +744,8 @@ extend_qtc_plugin(QmlDesigner assetimportupdatetreeitemdelegate.cpp assetimportupdatetreeitemdelegate.h assetimportupdatetreemodel.cpp assetimportupdatetreemodel.h assetimportupdatetreeview.cpp assetimportupdatetreeview.h + import3dcanvas.cpp import3dcanvas.h + import3dconnectionmanager.cpp import3dconnectionmanager.h itemlibrary.qrc itemlibraryconstants.h itemlibraryimageprovider.cpp itemlibraryimageprovider.h @@ -826,7 +833,7 @@ extend_qtc_plugin(QmlDesigner contentlibrarymaterialscategory.cpp contentlibrarymaterialscategory.h contentlibrarymaterial.cpp contentlibrarymaterial.h contentlibraryiconprovider.cpp contentlibraryiconprovider.h - contentlibraryeffect.cpp contentlibraryeffect.h + contentlibraryitem.cpp contentlibraryitem.h contentlibraryeffectscategory.cpp contentlibraryeffectscategory.h contentlibraryeffectsmodel.cpp contentlibraryeffectsmodel.h contentlibraryusermodel.cpp contentlibraryusermodel.h @@ -843,21 +850,6 @@ extend_qtc_plugin(QmlDesigner materialeditor.qrc ) -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 - collectioneditorconstants.h - collectioneditorutils.cpp collectioneditorutils.h - collectionlistmodel.cpp collectionlistmodel.h - collectionview.cpp collectionview.h - collectionwidget.cpp collectionwidget.h - datastoremodelnode.cpp datastoremodelnode.h -) - extend_qtc_plugin(QmlDesigner SOURCES_PREFIX components/textureeditor SOURCES diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp index 9d09f52d8ff..7f488bd6153 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp @@ -3,12 +3,15 @@ #include "assetslibrarymodel.h" -#include #include #include +#include #include + #include +#include +#include #include #include @@ -151,16 +154,15 @@ bool AssetsLibraryModel::renameFolder(const QString &folderPath, const QString & QString AssetsLibraryModel::addNewFolder(const QString &folderPath) { - QString iterPath = folderPath; - QDir dir{folderPath}; + Utils::FilePath uniqueDirPath = Utils::FilePath::fromString(UniqueName::generatePath(folderPath)); - while (dir.exists()) { - iterPath = getUniqueName(iterPath); - - dir.setPath(iterPath); + auto res = uniqueDirPath.ensureWritableDir(); + if (!res.has_value()) { + qWarning() << __FUNCTION__ << res.error(); + return {}; } - return dir.mkpath(iterPath) ? iterPath : ""; + return uniqueDirPath.path(); } bool AssetsLibraryModel::urlPathExistsInModel(const QUrl &url) const @@ -242,36 +244,6 @@ void AssetsLibraryModel::syncHasFiles() setHasFiles(checkHasFiles()); } -QString AssetsLibraryModel::getUniqueName(const QString &oldName) { - static QRegularExpression rgx("\\d+$"); // matches a number at the end of a string - - QString uniqueName = oldName; - // if the folder name ends with a number, increment it - QRegularExpressionMatch match = rgx.match(uniqueName); - if (match.hasMatch()) { // ends with a number - QString numStr = match.captured(0); - int num = match.captured(0).toInt(); - - // get number of padding zeros, ex: for "005" = 2 - int nPaddingZeros = 0; - for (; nPaddingZeros < numStr.size() && numStr[nPaddingZeros] == '0'; ++nPaddingZeros); - - ++num; - - // if the incremented number's digits increased, decrease the padding zeros - if (std::fmod(std::log10(num), 1.0) == 0) - --nPaddingZeros; - - uniqueName = oldName.mid(0, match.capturedStart()) - + QString('0').repeated(nPaddingZeros) - + QString::number(num); - } else { - uniqueName = oldName + '1'; - } - - return uniqueName; -} - void AssetsLibraryModel::setRootPath(const QString &newPath) { beginResetModel(); diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h index 2516be787fc..f08578651af 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h @@ -58,8 +58,6 @@ public: bool hasFiles() const { return m_hasFiles; } - QString getUniqueName(const QString &oldName); - signals: void directoryLoaded(const QString &path); void rootPathChanged(); diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp index 4b270c8902d..5e2211ce0d9 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp @@ -7,7 +7,6 @@ #include "assetslibrarymodel.h" #include "assetslibraryview.h" -#include #include #include #include @@ -18,6 +17,7 @@ #include #include #include +#include #include #include @@ -25,6 +25,7 @@ #include #include +#include #include #include #include @@ -94,7 +95,7 @@ AssetsLibraryWidget::AssetsLibraryWidget(AsynchronousImageCache &asynchronousFon , m_assetsModel{new AssetsLibraryModel(this)} , m_assetsView{view} , m_createTextures{view} - , m_assetsWidget{new StudioQuickWidget(this)} + , m_assetsWidget{Utils::makeUniqueObjectPtr(this)} { setWindowTitle(tr("Assets Library", "Title of assets library widget")); setMinimumWidth(250); @@ -130,7 +131,7 @@ AssetsLibraryWidget::AssetsLibraryWidget(AsynchronousImageCache &asynchronousFon auto layout = new QVBoxLayout(this); layout->setContentsMargins({}); layout->setSpacing(0); - layout->addWidget(m_assetsWidget.data()); + layout->addWidget(m_assetsWidget.get()); updateSearch(); @@ -174,23 +175,10 @@ void AssetsLibraryWidget::deleteSelectedAssets() QString AssetsLibraryWidget::getUniqueEffectPath(const QString &parentFolder, const QString &effectName) { - auto genEffectPath = [&parentFolder](const QString &name) { - QString effectsDir = ModelNodeOperations::getEffectsDefaultDirectory(parentFolder); - return QLatin1String("%1/%2.qep").arg(effectsDir, name); - }; + QString effectsDir = ModelNodeOperations::getEffectsDefaultDirectory(parentFolder); + QString effectPath = QLatin1String("%1/%2.qep").arg(effectsDir, effectName); - QString uniqueName = effectName; - QString path = genEffectPath(uniqueName); - QFileInfo file{path}; - - while (file.exists()) { - uniqueName = m_assetsModel->getUniqueName(uniqueName); - - path = genEffectPath(uniqueName); - file.setFile(path); - } - - return path; + return UniqueName::generatePath(effectPath); } bool AssetsLibraryWidget::createNewEffect(const QString &effectPath, bool openInEffectComposer) @@ -647,12 +635,6 @@ void AssetsLibraryWidget::addResources(const QStringList &files, bool showDialog } } -bool AssetsLibraryWidget::userBundleEnabled() const -{ - // TODO: this method is to be removed after user bundle implementation is complete - return Core::ICore::settings()->value("QML/Designer/UseExperimentalFeatures45", false).toBool(); -} - void AssetsLibraryWidget::addAssetsToContentLibrary(const QStringList &assetPaths) { m_assetsView->emitCustomNotification("add_assets_to_content_lib", {}, {assetPaths}); diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.h b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.h index 8b59ae07859..f2d476c8427 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.h +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.h @@ -8,6 +8,8 @@ #include +#include + #include #include #include @@ -98,7 +100,6 @@ public: Q_INVOKABLE void showInGraphicalShell(const QString &path); Q_INVOKABLE QString showInGraphicalShellMsg() const; - Q_INVOKABLE bool userBundleEnabled() const; Q_INVOKABLE void addAssetsToContentLibrary(const QStringList &assetPaths); signals: @@ -137,7 +138,7 @@ private: AssetsLibraryView *m_assetsView = nullptr; CreateTextures m_createTextures = nullptr; - QScopedPointer m_assetsWidget; + Utils::UniqueObjectPtr m_assetsWidget; std::unique_ptr m_fontPreviewTooltipBackend; QShortcut *m_qmlSourceUpdateShortcut = nullptr; diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondatatypemodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondatatypemodel.cpp deleted file mode 100644 index 9a534ec88e6..00000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondatatypemodel.cpp +++ /dev/null @@ -1,86 +0,0 @@ -// 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 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"}, -}; - -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::String; -} - -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 deleted file mode 100644 index 1f91aecbff7..00000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondatatypemodel.h +++ /dev/null @@ -1,34 +0,0 @@ -// 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 deleted file mode 100644 index a391d9bb1f3..00000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp +++ /dev/null @@ -1,965 +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 "collectiondetails.h" - -#include "collectiondatatypemodel.h" -#include "collectioneditorutils.h" - -#include -#include -#include -#include -#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 -{ - using DataType = CollectionDetails::DataType; - - QString name; - DataType type; -}; - -const QMap DataTypeWarning::dataTypeWarnings = { - {DataTypeWarning::CellDataTypeMismatch, "Cell and column data types do not match."} -}; - -class CollectionDetails::Private -{ -public: - QList properties; - 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 < dataRecords.size(); } -}; - -inline static bool isValidColorName(const QString &colorName) -{ - return QColor::isValidColorName(colorName); -} - -/** - * @brief getCustomUrl - * Address = - * - * @param value The input value to be evaluated - * @param dataType if the value is a valid url, the data type - * will be stored to this parameter, otherwise, it will be String - * @param urlResult if the value is a valid url, the address - * will be stored in this parameter, otherwise it will be empty. - * @return true if the result is url - */ -static bool getCustomUrl(const QString &value, - CollectionDetails::DataType &dataType, - QUrl *urlResult = nullptr) -{ - static const QRegularExpression urlRegex{ - "^(?
    " - "(?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.trimmed()); - if (match.hasCaptured("Address")) { - dataType = CollectionDetails::DataType::Url; - - if (urlResult) - urlResult->setUrl(match.captured("Address")); - - return true; - } - - if (urlResult) - urlResult->clear(); - - dataType = CollectionDetails::DataType::String; - return false; -} - -/** - * @brief dataTypeFromString - * @param value The string value to be evaluated - * @return 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::String; - - 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(trimmedValue, urlType)) - return urlType; - - return DataType::String; -} - -static CollectionProperty::DataType dataTypeFromJsonValue(const QJsonValue &value) -{ - using DataType = CollectionDetails::DataType; - using JsonType = QJsonValue::Type; - - switch (value.type()) { - case JsonType::Null: - case JsonType::Undefined: - return DataType::String; - case JsonType::Bool: - return DataType::Boolean; - case JsonType::Double: { - if (qFuzzyIsNull(std::remainder(value.toDouble(), 1))) - return DataType::Integer; - return DataType::Real; - } - case JsonType::String: - return dataTypeFromString(value.toString()); - default: - return DataType::String; - } -} - -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::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; - QVariant variantValue = value.toVariant(); - - switch (type) { - case DataType::String: - return variantValue.toString(); - case DataType::Integer: - return variantValue.toInt(); - case DataType::Real: - return variantValue.toDouble(); - case DataType::Boolean: - return variantValue.toBool(); - case DataType::Color: - return variantValue.value(); - case DataType::Url: - case DataType::Image: - return variantValue.value(); - default: - return variantValue; - } -} - -static QJsonValue variantToJsonValue( - const QVariant &variant, CollectionDetails::DataType type = CollectionDetails::DataType::String) -{ - using DataType = CollectionDetails::DataType; - - switch (type) { - case DataType::Boolean: - return variant.toBool(); - case DataType::Real: - return variant.toDouble(); - case DataType::Integer: - return variant.toInt(); - case DataType::Image: - case DataType::String: - case DataType::Color: - case DataType::Url: - default: - return variant.toString(); - } -} - -inline static bool isEmptyJsonValue(const QJsonValue &value) -{ - return value.isNull() || value.isUndefined() || (value.isString() && value.toString().isEmpty()); -} - -QStringList csvReadLine(const QString &line) -{ - 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); - - QStringList result; - QRegularExpressionMatchIterator iterator = lineRegex.globalMatch(line, 0); - while (iterator.hasNext()) { - const QRegularExpressionMatch match = iterator.next(); - - if (match.hasCaptured(valueIndex)) - result.append(match.captured(valueIndex)); - else if (match.hasCaptured(quoteIndex)) - result.append(match.captured(quoteIndex)); - } - return result; -} - -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) { - 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()) -{} - -CollectionDetails::CollectionDetails(const CollectionReference &reference) - : CollectionDetails() -{ - 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::insertColumn(const QString &propertyName, - int colIdx, - const QVariant &defaultValue, - DataType type) -{ - if (containsPropertyName(propertyName)) - return; - - CollectionProperty property = {propertyName, type}; - if (d->isValidColumnId(colIdx)) { - d->properties.insert(colIdx, property); - } else { - colIdx = d->properties.size(); - d->properties.append(property); - } - - const QJsonValue defaultJsonValue = QJsonValue::fromVariant(defaultValue); - for (QJsonArray &record : d->dataRecords) - record.insert(colIdx, defaultJsonValue); - - markChanged(); -} - -bool CollectionDetails::removeColumns(int colIdx, int count) -{ - if (!d->isValidColumnId(colIdx)) - return false; - - int maxCount = d->properties.count() - colIdx; - count = std::min(maxCount, count); - - if (count < 1) - return false; - - d->properties.remove(colIdx, count); - - 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(); - - return true; -} - -void CollectionDetails::insertEmptyRows(int row, int count) -{ - if (count < 1) - return; - - row = qBound(0, row, rows()); - - insertRecords({}, row, count); - - markChanged(); -} - -bool CollectionDetails::removeRows(int row, int count) -{ - if (!d->isValidRowId(row)) - return false; - - int maxCount = d->dataRecords.count() - row; - count = std::min(maxCount, count); - - if (count < 1) - return false; - - d->dataRecords.remove(row, count); - markChanged(); - return true; -} - -bool CollectionDetails::setPropertyValue(int row, int column, const QVariant &value) -{ - if (!d->isValidRowId(row) || !d->isValidColumnId(column)) - return false; - - QVariant currentValue = data(row, column); - if (value == currentValue) - return false; - - QJsonArray &record = d->dataRecords[row]; - record.replace(column, variantToJsonValue(value, typeAt(column))); - markChanged(); - return true; -} - -bool CollectionDetails::setPropertyName(int column, const QString &value) -{ - if (!d->isValidColumnId(column)) - return false; - - const CollectionProperty &oldProperty = d->properties.at(column); - if (oldProperty.name == value) - return false; - - d->properties.replace(column, {value, oldProperty.type}); - - markChanged(); - return true; -} - -bool CollectionDetails::setPropertyType(int column, DataType type) -{ - if (!d->isValidColumnId(column)) - return false; - - bool changed = false; - CollectionProperty &property = d->properties[column]; - if (property.type != type) - changed = true; - - const DataType formerType = property.type; - property.type = type; - - 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); - rowData.replace(column, properTypedJsonValue); - changed = true; - } - } - - if (changed) - markChanged(); - - return changed; -} - -CollectionReference CollectionDetails::reference() const -{ - return d->reference; -} - -QVariant CollectionDetails::data(int row, int column) const -{ - if (!d->isValidRowId(row)) - return {}; - - if (!d->isValidColumnId(column)) - return {}; - - const QJsonValue cellValue = d->dataRecords.at(row).at(column); - - return cellValue.toVariant(); -} - -QString CollectionDetails::propertyAt(int column) const -{ - if (!d->isValidColumnId(column)) - return {}; - - return d->properties.at(column).name; -} - -CollectionDetails::DataType CollectionDetails::typeAt(int column) const -{ - if (!d->isValidColumnId(column)) - return {}; - - return d->properties.at(column).type; -} - -CollectionDetails::DataType CollectionDetails::typeAt(int row, int column) const -{ - if (!d->isValidRowId(row) || !d->isValidColumnId(column)) - return {}; - - const QJsonValue cellData = d->dataRecords.at(row).at(column); - return dataTypeFromJsonValue(cellData); -} - -DataTypeWarning::Warning CollectionDetails::cellWarningCheck(int row, int column) const -{ - const QJsonValue cellValue = d->dataRecords.at(row).at(column); - - const DataType columnType = typeAt(column); - const DataType cellType = typeAt(row, column); - - if (isEmptyJsonValue(cellValue)) - return DataTypeWarning::Warning::None; - - if ((columnType == DataType::String || columnType == DataType::Real) && cellType == DataType::Integer) - return DataTypeWarning::Warning::None; - - if ((columnType == DataType::Url || columnType == DataType::Image) && cellType == DataType::String) - return DataTypeWarning::Warning::None; - - if (columnType != cellType) - return DataTypeWarning::Warning::CellDataTypeMismatch; - - return DataTypeWarning::Warning::None; -} - -bool CollectionDetails::containsPropertyName(const QString &propertyName) const -{ - return Utils::anyOf(d->properties, [&propertyName](const CollectionProperty &property) { - return property.name == propertyName; - }); -} - -bool CollectionDetails::hasValidReference() const -{ - return d->reference.node.isValid() && d->reference.name.size(); -} - -bool CollectionDetails::isChanged() const -{ - return d->isChanged; -} - -int CollectionDetails::columns() const -{ - return d->properties.size(); -} - -int CollectionDetails::rows() const -{ - return d->dataRecords.size(); -} - -bool CollectionDetails::markSaved() -{ - if (d->isChanged) { - d->isChanged = false; - return true; - } - return false; -} - -void CollectionDetails::swap(CollectionDetails &other) -{ - d.swap(other.d); -} - -void CollectionDetails::resetReference(const CollectionReference &reference) -{ - if (d->reference != reference) { - d->reference = reference; - markChanged(); - } -} - -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, QJsonValue::Null); - else - 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 if (value.isBool()) - content += value.toBool() ? QString("true,") : QString("false,"); - 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", CollectionDataTypeModel::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; - qRegisterMetaType("DataType"); - qmlRegisterUncreatableType("CollectionDetails", 1, 0, "DataType", "Enum type"); - - qRegisterMetaType("Warning"); - qmlRegisterUncreatableType("CollectionDetails", 1, 0, "Warning", "Enum type"); -} - -CollectionDetails CollectionDetails::fromImportedCsv(const QByteArray &document, - const bool &firstRowIsHeader) -{ - QStringList headers; - QJsonArray importedArray; - - QTextStream stream(document); - stream.setEncoding(QStringConverter::Latin1); - - if (firstRowIsHeader && !stream.atEnd()) { - headers = Utils::transform(csvReadLine(stream.readLine()), - [](const QString &value) -> QString { return value.trimmed(); }); - } - - while (!stream.atEnd()) { - const QStringList recordDataList = csvReadLine(stream.readLine()); - int column = -1; - QJsonObject recordData; - for (const QString &cellData : recordDataList) { - if (++column == headers.size()) { - QString proposalName; - int proposalId = column; - do - proposalName = QString("Column %1").arg(++proposalId); - while (headers.contains(proposalName)); - headers.append(proposalName); - } - recordData.insert(headers.at(column), cellData); - } - importedArray.append(recordData); - } - - return fromImportedJson(importedArray, headers); -} - -CollectionDetails CollectionDetails::fromImportedJson(const QByteArray &json, QJsonParseError *error) -{ - 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; - }; - - 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()) { - 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, PropertyOrderFinder::parse(QLatin1String(json))); -} - -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); - swap(value); - return *this; -} - -void CollectionDetails::markChanged() -{ - d->isChanged = true; -} - -void CollectionDetails::insertRecords(const QJsonArray &record, int idx, int count) -{ - 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); -} - -CollectionDetails CollectionDetails::fromImportedJson(const QJsonArray &importedArray, - const QStringList &propertyPriority) -{ - 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(); - 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(); - - return result; -} - -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, - {}, - CollectionDataTypeModel::dataTypeFromString( - column.value("type").toString())); - } - } - } - - if (int columnsCount = result.columns()) { - const QJsonArray dataRecords = localCollection.value("data").toArray(); - for (const QJsonValue &dataRecordValue : dataRecords) { - QJsonArray dataRecord = dataRecordValue.toArray(); - while (dataRecord.count() > columnsCount) - dataRecord.removeLast(); - - result.insertRecords(dataRecord); - } - } - } else { - setError(CollectionParseError::ColumnsBlockIsNotArray); - return result; - } - } - - return result; -} - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h deleted file mode 100644 index 7243c585c6b..00000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h +++ /dev/null @@ -1,152 +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 - -#pragma once - -#include "modelnode.h" - -#include - -QT_BEGIN_NAMESPACE -class QJsonObject; -struct QJsonParseError; -class QVariant; -QT_END_NAMESPACE - -namespace QmlDesigner { - -struct CollectionReference -{ - ModelNode node; - QString name; - - friend auto qHash(const CollectionReference &collection) - { - return qHash(collection.node) ^ ::qHash(collection.name); - } - - bool operator==(const CollectionReference &other) const - { - return node == other.node && name == other.name; - } - - bool operator!=(const CollectionReference &other) const { return !(*this == other); } -}; - -struct CollectionProperty; - -struct DataTypeWarning { -public: - enum Warning { None, CellDataTypeMismatch }; - Q_ENUM(Warning) - - Warning warning = None; - DataTypeWarning(Warning warning) - : 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 - -public: - enum class DataType { Unknown, String, Url, Integer, Real, Boolean, Image, Color }; - Q_ENUM(DataType) - - explicit CollectionDetails(); - CollectionDetails(const CollectionReference &reference); - CollectionDetails(const CollectionDetails &other); - ~CollectionDetails(); - - 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::String); - bool removeColumns(int colIdx, 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; - 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) const; - - bool hasValidReference() const; - bool isChanged() const; - - int columns() const; - int rows() const; - - bool markSaved(); - - void swap(CollectionDetails &other); - void resetReference(const CollectionReference &reference); - - QString toJson() const; - QString toCsv() const; - QJsonObject toLocalJson() const; - - static void registerDeclarativeType(); - - static CollectionDetails fromImportedCsv(const QByteArray &document, - const bool &firstRowIsHeader = true); - static CollectionDetails fromImportedJson(const QByteArray &json, - QJsonParseError *error = nullptr); - static CollectionDetails fromLocalJson(const QJsonDocument &document, - const QString &collectionName, - CollectionParseError *error = nullptr); - - CollectionDetails &operator=(const CollectionDetails &other); - -private: - void markChanged(); - void insertRecords(const QJsonArray &record, int idx = -1, int count = 1); - - static CollectionDetails fromImportedJson(const QJsonArray &importedArray, - const QStringList &propertyPriority = {}); - 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 deleted file mode 100644 index fcd6d686efd..00000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp +++ /dev/null @@ -1,627 +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 "collectiondetailsmodel.h" - -#include "collectiondatatypemodel.h" -#include "collectioneditorutils.h" -#include "modelnode.h" - -#include - -#include -#include -#include - -#include -#include -#include -#include - -namespace QmlDesigner { - -CollectionDetailsModel::CollectionDetailsModel(QObject *parent) - : QAbstractTableModel(parent) -{ - connect(this, &CollectionDetailsModel::modelReset, this, &CollectionDetailsModel::updateEmpty); - connect(this, &CollectionDetailsModel::rowsInserted, this, &CollectionDetailsModel::updateEmpty); - connect(this, &CollectionDetailsModel::rowsRemoved, this, &CollectionDetailsModel::updateEmpty); -} - -QHash CollectionDetailsModel::roleNames() const -{ - static QHash roles; - if (roles.isEmpty()) { - roles.insert(QAbstractTableModel::roleNames()); - roles.insert(SelectedRole, "itemSelected"); - roles.insert(DataTypeRole, "dataType"); - roles.insert(ColumnDataTypeRole, "columnType"); - roles.insert(DataTypeWarningRole, "dataTypeWarning"); - } - return roles; -} - -int CollectionDetailsModel::rowCount([[maybe_unused]] const QModelIndex &parent) const -{ - return m_currentCollection.rows(); -} - -int CollectionDetailsModel::columnCount([[maybe_unused]] const QModelIndex &parent) const -{ - return m_currentCollection.columns(); -} - -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); - - if (role == DataTypeRole) - return QVariant::fromValue(m_currentCollection.typeAt(index.row(), index.column())); - - if (role == ColumnDataTypeRole) - return QVariant::fromValue(m_currentCollection.typeAt(index.column())); - - if (role == Qt::EditRole) - return m_currentCollection.data(index.row(), index.column()); - - if (role == DataTypeWarningRole ) - return QVariant::fromValue(m_currentCollection.cellWarningCheck(index.row(), index.column())); - - return m_currentCollection.data(index.row(), index.column()).toString(); -} - -bool CollectionDetailsModel::setData(const QModelIndex &index, const QVariant &value, int role) -{ - QTC_ASSERT(m_currentCollection.hasValidReference(), return false); - - if (!index.isValid()) - return {}; - - if (role == Qt::EditRole) { - DataTypeWarning::Warning prevWarning = m_currentCollection.cellWarningCheck(index.row(), index.column()); - bool changed = m_currentCollection.setPropertyValue(index.row(), index.column(), value); - - if (changed) { - QList roles = {Qt::DisplayRole, Qt::EditRole}; - - if (prevWarning != m_currentCollection.cellWarningCheck(index.row(), index.column())) - roles << DataTypeWarningRole; - - setHasUnsavedChanges(true); - emit dataChanged(index, index, roles); - } - - return true; - } - - return false; -} - -bool CollectionDetailsModel::setHeaderData(int section, - Qt::Orientation orientation, - const QVariant &value, - [[maybe_unused]] int role) -{ - QTC_ASSERT(m_currentCollection.hasValidReference(), return false); - - if (orientation == Qt::Vertical) - return false; - - bool headerChanged = m_currentCollection.setPropertyName(section, value.toString()); - if (headerChanged) - emit this->headerDataChanged(orientation, section, section); - - return headerChanged; -} - -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()); - - beginInsertRows({}, row, row + count - 1); - m_currentCollection.insertEmptyRows(row, count); - endInsertRows(); - setHasUnsavedChanges(true); - - return true; -} - -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; - - count = std::min(count, columnCount(parent) - column); - beginRemoveColumns(parent, column, column + count - 1); - bool columnsRemoved = m_currentCollection.removeColumns(column, count); - endRemoveColumns(); - - if (!columnCount(parent)) - removeRows(0, rowCount(parent), parent); - - ensureSingleCell(); - return columnsRemoved; -} - -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.removeRows(row, count); - endRemoveRows(); - - ensureSingleCell(); - return rowsRemoved; -} - -Qt::ItemFlags CollectionDetailsModel::flags(const QModelIndex &index) const -{ - if (!index.isValid()) - return {}; - - return {Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable}; -} - -QVariant CollectionDetailsModel::headerData(int section, Qt::Orientation orientation, int role) const -{ - if (orientation == Qt::Horizontal) { - if (role == DataTypeRole) - return CollectionDataTypeModel::dataTypeToString(m_currentCollection.typeAt(section)); - else - return m_currentCollection.propertyAt(section); - } - - if (orientation == Qt::Vertical) - return section + 1; - - return {}; -} - -CollectionDetails::DataType CollectionDetailsModel::propertyDataType(int column) const -{ - QTC_ASSERT(m_currentCollection.hasValidReference(), return CollectionDetails::DataType::String); - - return m_currentCollection.typeAt(column); -} - -int CollectionDetailsModel::selectedColumn() const -{ - return m_selectedColumn; -} - -int CollectionDetailsModel::selectedRow() const -{ - return m_selectedRow; -} - -QString CollectionDetailsModel::propertyName(int column) const -{ - QTC_ASSERT(m_currentCollection.hasValidReference(), return {}); - - return m_currentCollection.propertyAt(column); -} - -QString CollectionDetailsModel::propertyType(int column) const -{ - QTC_ASSERT(m_currentCollection.hasValidReference(), return {}); - - return CollectionDataTypeModel::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; - - if (column < 0 || column > columnCount()) - column = columnCount(); - - beginInsertColumns({}, column, column); - m_currentCollection.insertColumn(name, - column, - {}, - CollectionDataTypeModel::dataTypeFromString(propertyType)); - endInsertColumns(); - setHasUnsavedChanges(true); - return m_currentCollection.containsPropertyName(name); -} - -bool CollectionDetailsModel::selectColumn(int section) -{ - if (m_selectedColumn == section) - return false; - - const int columns = columnCount(); - - if (section >= columns) - section = columns - 1; - - selectRow(-1); - - const int rows = rowCount(); - const int previousColumn = m_selectedColumn; - - m_selectedColumn = section; - emit this->selectedColumnChanged(m_selectedColumn); - - auto notifySelectedDataChanged = [this, columns, rows](int notifyingColumn) { - if (notifyingColumn > -1 && notifyingColumn < columns && rows) { - emit dataChanged(index(0, notifyingColumn), - index(rows - 1, notifyingColumn), - {SelectedRole}); - } - }; - - notifySelectedDataChanged(previousColumn); - notifySelectedDataChanged(m_selectedColumn); - - return true; -} - -bool CollectionDetailsModel::renameColumn(int section, const QString &newValue) -{ - return setHeaderData(section, Qt::Horizontal, newValue); -} - -bool CollectionDetailsModel::setPropertyType(int column, const QString &newValue) -{ - QTC_ASSERT(m_currentCollection.hasValidReference(), return false); - - bool changed = m_currentCollection.setPropertyType(column, - CollectionDataTypeModel::dataTypeFromString( - newValue)); - if (changed) { - emit headerDataChanged(Qt::Horizontal, column, column); - emit dataChanged( - index(0, column), - index(rowCount() - 1, column), - {Qt::DisplayRole, Qt::EditRole, DataTypeRole, DataTypeWarningRole, ColumnDataTypeRole}); - } - - setHasUnsavedChanges(true); - return changed; -} - -bool CollectionDetailsModel::selectRow(int row) -{ - if (m_selectedRow == row) - return false; - - const int rows = rowCount(); - - if (row >= rows) - row = rows - 1; - - selectColumn(-1); - - const int columns = columnCount(); - const int previousRow = m_selectedRow; - - m_selectedRow = row; - emit this->selectedRowChanged(m_selectedRow); - - auto notifySelectedDataChanged = [this, rows, columns](int notifyingRow) { - if (notifyingRow > -1 && notifyingRow < rows && columns) - emit dataChanged(index(notifyingRow, 0), index(notifyingRow, columns - 1), {SelectedRole}); - }; - - notifySelectedDataChanged(previousRow); - notifySelectedDataChanged(m_selectedRow); - - return true; -} - -void CollectionDetailsModel::deselectAll() -{ - selectColumn(-1); - selectRow(-1); -} - -void CollectionDetailsModel::loadCollection(const ModelNode &sourceNode, const QString &collection) -{ - QString fileName = CollectionEditorUtils::getSourceCollectionPath(sourceNode); - - CollectionReference newReference{sourceNode, collection}; - bool alreadyOpen = m_openedCollections.contains(newReference); - - if (alreadyOpen) { - if (m_currentCollection.reference() != newReference) { - deselectAll(); - beginResetModel(); - switchToCollection(newReference); - ensureSingleCell(); - endResetModel(); - } - } else { - deselectAll(); - switchToCollection(newReference); - loadJsonCollection(fileName, collection); - } -} - -void CollectionDetailsModel::removeCollection(const ModelNode &sourceNode, const QString &collection) -{ - CollectionReference collectionRef{sourceNode, collection}; - if (!m_openedCollections.contains(collectionRef)) - return; - - if (m_currentCollection.reference() == collectionRef) - loadCollection({}, {}); - - m_openedCollections.remove(collectionRef); -} - -void CollectionDetailsModel::removeAllCollections() -{ - loadCollection({}, {}); - m_openedCollections.clear(); -} - -void CollectionDetailsModel::renameCollection(const ModelNode &sourceNode, - const QString &oldName, - const QString &newName) -{ - CollectionReference oldRef{sourceNode, oldName}; - if (!m_openedCollections.contains(oldRef)) - return; - - CollectionReference newReference{sourceNode, newName}; - bool collectionIsSelected = m_currentCollection.reference() == oldRef; - CollectionDetails collection = m_openedCollections.take(oldRef); - collection.resetReference(newReference); - m_openedCollections.insert(newReference, collection); - - if (collectionIsSelected) - setCollectionName(newName); -} - -bool CollectionDetailsModel::saveDataStoreCollections() -{ - const ModelNode node = m_currentCollection.reference().node; - const Utils::FilePath path = CollectionEditorUtils::dataStoreJsonFilePath(); - Utils::FileReader fileData; - - if (!fileData.fetch(path)) { - qWarning() << Q_FUNC_INFO << "Cannot read the json file:" << fileData.errorString(); - return false; - } - - QJsonParseError jpe; - QJsonDocument document = QJsonDocument::fromJson(fileData.data(), &jpe); - - if (jpe.error == QJsonParseError::NoError) { - QJsonObject obj = document.object(); - - QList collectionsToBeSaved; - for (CollectionDetails &openedCollection : m_openedCollections) { - const CollectionReference reference = openedCollection.reference(); - if (reference.node == node) { - obj.insert(reference.name, openedCollection.toLocalJson()); - collectionsToBeSaved << openedCollection; - } - } - - document.setObject(obj); - - if (CollectionEditorUtils::writeToJsonDocument(path, document)) { - const CollectionReference currentReference = m_currentCollection.reference(); - for (CollectionDetails &collection : collectionsToBeSaved) { - collection.markSaved(); - const CollectionReference reference = collection.reference(); - if (reference != currentReference) - closeCollectionIfSaved(reference); - } - setHasUnsavedChanges(false); - return true; - } - } - return false; -} - -bool CollectionDetailsModel::exportCollection(const QUrl &url) -{ - using Core::EditorManager; - using Utils::FilePath; - using Utils::TextFileFormat; - - 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.toCsv() - : m_currentCollection.toJson(); - - TextFileFormat textFileFormat; - textFileFormat.codec = EditorManager::defaultTextCodec(); - textFileFormat.lineTerminationMode = EditorManager::defaultLineEnding(); - QString errorMessage; - saved = textFileFormat.writeFile(filePath, content, &errorMessage); - - if (!saved) - qWarning() << Q_FUNC_INFO << "Unable to write file" << errorMessage; - - 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; - if (m_isEmpty != isEmptyNow) { - m_isEmpty = isEmptyNow; - emit isEmptyChanged(m_isEmpty); - } -} - -void CollectionDetailsModel::switchToCollection(const CollectionReference &collection) -{ - if (m_currentCollection.reference() == collection) - return; - - closeCurrentCollectionIfSaved(); - - if (!m_openedCollections.contains(collection)) - m_openedCollections.insert(collection, CollectionDetails(collection)); - - m_currentCollection = m_openedCollections.value(collection); - - setCollectionName(collection.name); -} - -void CollectionDetailsModel::closeCollectionIfSaved(const CollectionReference &collection) -{ - if (!m_openedCollections.contains(collection)) - return; - - const CollectionDetails &collectionDetails = m_openedCollections.value(collection); - - if (!collectionDetails.isChanged()) - m_openedCollections.remove(collection); -} - -void CollectionDetailsModel::closeCurrentCollectionIfSaved() -{ - if (m_currentCollection.hasValidReference()) { - closeCollectionIfSaved(m_currentCollection.reference()); - m_currentCollection = CollectionDetails{}; - } -} - -void CollectionDetailsModel::loadJsonCollection(const QString &filePath, const QString &collection) -{ - QJsonDocument document = readJsonFile(filePath); - - beginResetModel(); - m_currentCollection.resetData(document, collection); - ensureSingleCell(); - endResetModel(); -} - -void CollectionDetailsModel::ensureSingleCell() -{ - if (!m_currentCollection.hasValidReference()) - return; - - if (!columnCount()) - addColumn(0, "Column 1", "String"); - - if (!rowCount()) - insertRow(0); - - updateEmpty(); -} - -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) { - m_collectionName = newCollectionName; - emit this->collectionNameChanged(m_collectionName); - } -} - -QString CollectionDetailsModel::warningToString(DataTypeWarning::Warning warning) const -{ - return DataTypeWarning::getDataTypeWarningString(warning); -} - -void CollectionDetailsModel::setHasUnsavedChanges(bool val) -{ - if (m_hasUnsavedChanges == val) - return; - m_hasUnsavedChanges = val; - emit hasUnsavedChangesChanged(); -} - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h deleted file mode 100644 index 8844ff4a3ef..00000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h +++ /dev/null @@ -1,106 +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 - -#pragma once - -#include "collectiondetails.h" - -#include -#include - -namespace QmlDesigner { - -class ModelNode; - -class CollectionDetailsModel : public QAbstractTableModel -{ - Q_OBJECT - - Q_PROPERTY(QString collectionName MEMBER m_collectionName NOTIFY collectionNameChanged) - Q_PROPERTY(int selectedColumn READ selectedColumn WRITE selectColumn NOTIFY selectedColumnChanged) - Q_PROPERTY(int selectedRow READ selectedRow WRITE selectRow NOTIFY selectedRowChanged) - Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged) - Q_PROPERTY(bool hasUnsavedChanges MEMBER m_hasUnsavedChanges WRITE setHasUnsavedChanges NOTIFY hasUnsavedChangesChanged) - -public: - enum DataRoles { SelectedRole = Qt::UserRole + 1, DataTypeRole, ColumnDataTypeRole, DataTypeWarningRole }; - explicit CollectionDetailsModel(QObject *parent = nullptr); - - QHash roleNames() const override; - int rowCount(const QModelIndex &parent = {}) const override; - int columnCount(const QModelIndex &parent = {}) const override; - QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; - bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; - bool setHeaderData(int section, - Qt::Orientation orientation, - const QVariant &value, - int role = Qt::EditRole) override; - bool insertRows(int row, int count, const QModelIndex &parent = {}) override; - bool removeColumns(int column, int count, const QModelIndex &parent = {}) override; - bool removeRows(int row, int count, const QModelIndex &parent = {}) override; - - Qt::ItemFlags flags(const QModelIndex &index) const override; - QVariant headerData(int section, - Qt::Orientation orientation, - int role = Qt::DisplayRole) const override; - - CollectionDetails::DataType propertyDataType(int column) const; - - int selectedColumn() const; - int selectedRow() const; - Q_INVOKABLE QString propertyName(int column) const; - Q_INVOKABLE QString propertyType(int column) const; - - Q_INVOKABLE bool isPropertyAvailable(const QString &name); - Q_INVOKABLE bool addColumn(int column, const QString &name, const QString &propertyType = {}); - Q_INVOKABLE bool selectColumn(int section); - Q_INVOKABLE bool renameColumn(int section, const QString &newValue); - Q_INVOKABLE bool setPropertyType(int column, const QString &newValue); - Q_INVOKABLE bool selectRow(int row); - Q_INVOKABLE void deselectAll(); - Q_INVOKABLE QString warningToString(DataTypeWarning::Warning warning) const; - - void loadCollection(const ModelNode &sourceNode, const QString &collection); - void removeCollection(const ModelNode &sourceNode, const QString &collection); - void removeAllCollections(); - void renameCollection(const ModelNode &sourceNode, const QString &oldName, const QString &newName); - - 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; - void setHasUnsavedChanges(bool val); - -signals: - void collectionNameChanged(const QString &collectionName); - void selectedColumnChanged(int); - void selectedRowChanged(int); - void isEmptyChanged(bool); - void hasUnsavedChangesChanged(); - void warning(const QString &title, const QString &body); - -private slots: - void updateEmpty(); - -private: - void switchToCollection(const CollectionReference &collection); - void closeCollectionIfSaved(const CollectionReference &collection); - void closeCurrentCollectionIfSaved(); - void setCollectionName(const QString &newCollectionName); - void loadJsonCollection(const QString &filePath, const QString &collection); - void ensureSingleCell(); - QJsonDocument readJsonFile(const QUrl &url); - - QHash m_openedCollections; - CollectionDetails m_currentCollection; - bool m_isEmpty = true; - bool m_hasUnsavedChanges = false; - int m_selectedColumn = -1; - int m_selectedRow = -1; - - QString m_collectionName; -}; - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.cpp deleted file mode 100644 index 2cc6ac05a6b..00000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.cpp +++ /dev/null @@ -1,163 +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 "collectiondetailssortfiltermodel.h" - -#include "collectiondetailsmodel.h" -#include "collectioneditorutils.h" - -#include - -namespace QmlDesigner { - -CollectionDetailsSortFilterModel::CollectionDetailsSortFilterModel(QObject *parent) - : QSortFilterProxyModel(parent) -{ - connect(this, &CollectionDetailsSortFilterModel::rowsInserted, - this, &CollectionDetailsSortFilterModel::updateRowCountChanges); - connect(this, &CollectionDetailsSortFilterModel::rowsRemoved, - this, &CollectionDetailsSortFilterModel::updateRowCountChanges); - connect(this, &CollectionDetailsSortFilterModel::modelReset, - this, &CollectionDetailsSortFilterModel::updateRowCountChanges); - - setDynamicSortFilter(true); -} - -void CollectionDetailsSortFilterModel::setSourceModel(CollectionDetailsModel *model) -{ - m_source = model; - Super::setSourceModel(model); - connect(m_source, &CollectionDetailsModel::selectedColumnChanged, - this, &CollectionDetailsSortFilterModel::updateSelectedColumn); - - connect(m_source, &CollectionDetailsModel::selectedRowChanged, - this, &CollectionDetailsSortFilterModel::updateSelectedRow); -} - -int CollectionDetailsSortFilterModel::selectedRow() const -{ - QTC_ASSERT(m_source, return -1); - - return mapFromSource(m_source->index(m_source->selectedRow(), 0)).row(); -} - -int CollectionDetailsSortFilterModel::selectedColumn() const -{ - QTC_ASSERT(m_source, return -1); - - return mapFromSource(m_source->index(0, m_source->selectedColumn())).column(); -} - -bool CollectionDetailsSortFilterModel::selectRow(int row) -{ - QTC_ASSERT(m_source, return false); - - return m_source->selectRow(mapToSource(index(row, 0)).row()); -} - -bool CollectionDetailsSortFilterModel::selectColumn(int column) -{ - QTC_ASSERT(m_source, return false); - - return m_source->selectColumn(mapToSource(index(0, column)).column()); -} - -void CollectionDetailsSortFilterModel::deselectAll() -{ - QTC_ASSERT(m_source, return); - m_source->deselectAll(); -} - -CollectionDetailsSortFilterModel::~CollectionDetailsSortFilterModel() = default; - -bool CollectionDetailsSortFilterModel::filterAcceptsRow(int sourceRow, - const QModelIndex &sourceParent) const -{ - QTC_ASSERT(m_source, return false); - QModelIndex sourceIndex(m_source->index(sourceRow, 0, sourceParent)); - return sourceIndex.isValid(); -} - -bool CollectionDetailsSortFilterModel::lessThan(const QModelIndex &sourceleft, - const QModelIndex &sourceRight) const -{ - QTC_ASSERT(m_source, return false); - - if (sourceleft.column() == sourceRight.column()) { - int column = sourceleft.column(); - CollectionDetails::DataType columnType = m_source->propertyDataType(column); - return CollectionEditorUtils::variantIslessThan(sourceleft.data(), - sourceRight.data(), - columnType); - } - - return false; -} - -void CollectionDetailsSortFilterModel::updateEmpty() -{ - bool newValue = rowCount() == 0; - if (m_isEmpty != newValue) { - m_isEmpty = newValue; - emit isEmptyChanged(m_isEmpty); - } -} - -void CollectionDetailsSortFilterModel::updateSelectedRow() -{ - const int upToDateSelectedRow = selectedRow(); - if (m_selectedRow == upToDateSelectedRow) - return; - - const int rows = rowCount(); - const int columns = columnCount(); - const int previousRow = m_selectedRow; - - m_selectedRow = upToDateSelectedRow; - emit this->selectedRowChanged(m_selectedRow); - - auto notifySelectedDataChanged = [this, rows, columns](int notifyingRow) { - if (notifyingRow > -1 && notifyingRow < rows && columns) { - emit dataChanged(index(notifyingRow, 0), - index(notifyingRow, columns - 1), - {CollectionDetailsModel::SelectedRole}); - } - }; - - notifySelectedDataChanged(previousRow); - notifySelectedDataChanged(m_selectedRow); -} - -void CollectionDetailsSortFilterModel::updateSelectedColumn() -{ - const int upToDateSelectedColumn = selectedColumn(); - if (m_selectedColumn == upToDateSelectedColumn) - return; - - const int rows = rowCount(); - const int columns = columnCount(); - const int previousColumn = m_selectedColumn; - - m_selectedColumn = upToDateSelectedColumn; - emit this->selectedColumnChanged(m_selectedColumn); - - auto notifySelectedDataChanged = [this, rows, columns](int notifyingCol) { - if (notifyingCol > -1 && notifyingCol < columns && rows) { - emit dataChanged(index(0, notifyingCol), - index(rows - 1, notifyingCol), - {CollectionDetailsModel::SelectedRole}); - } - }; - - notifySelectedDataChanged(previousColumn); - notifySelectedDataChanged(m_selectedColumn); -} - -void CollectionDetailsSortFilterModel::updateRowCountChanges() -{ - updateEmpty(); - updateSelectedRow(); - invalidate(); -} - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.h b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.h deleted file mode 100644 index 10f6e09b057..00000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.h +++ /dev/null @@ -1,58 +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 - -#pragma once - -#include -#include - -namespace QmlDesigner { - -class CollectionDetailsModel; - -class CollectionDetailsSortFilterModel : public QSortFilterProxyModel -{ - Q_OBJECT - - Q_PROPERTY(int selectedColumn READ selectedColumn WRITE selectColumn NOTIFY selectedColumnChanged) - Q_PROPERTY(int selectedRow READ selectedRow WRITE selectRow NOTIFY selectedRowChanged) - Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged) - - using Super = QSortFilterProxyModel; - -public: - explicit CollectionDetailsSortFilterModel(QObject *parent = nullptr); - virtual ~CollectionDetailsSortFilterModel(); - - void setSourceModel(CollectionDetailsModel *model); - - int selectedRow() const; - int selectedColumn() const; - - Q_INVOKABLE bool selectRow(int row); - Q_INVOKABLE bool selectColumn(int column); - Q_INVOKABLE void deselectAll(); - -signals: - void selectedColumnChanged(int); - void selectedRowChanged(int); - void isEmptyChanged(bool); - -protected: - using Super::setSourceModel; - bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override; - bool lessThan(const QModelIndex &sourceleft, const QModelIndex &sourceRight) const override; - -private: - void updateEmpty(); - void updateSelectedRow(); - void updateSelectedColumn(); - void updateRowCountChanges(); - - QPointer m_source; - int m_selectedColumn = -1; - int m_selectedRow = -1; - bool m_isEmpty = true; -}; - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorconstants.h b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorconstants.h deleted file mode 100644 index 76524762ede..00000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorconstants.h +++ /dev/null @@ -1,19 +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 - -#pragma once - -namespace QmlDesigner::CollectionEditorConstants { - -enum class SourceFormat { Unknown, Json }; - -inline constexpr char SOURCEFILE_PROPERTY[] = "source"; -inline constexpr char ALLMODELS_PROPERTY[] = "allModels"; -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 JSONCOLLECTIONCHILDMODEL_TYPENAME[] = "QtQuick.Studio.Utils.ChildListModel"; -inline constexpr char JSONBACKEND_TYPENAME[] = "JsonData"; - -} // namespace QmlDesigner::CollectionEditorConstants diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp deleted file mode 100644 index 29b833cc2ce..00000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp +++ /dev/null @@ -1,345 +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 "collectioneditorutils.h" - -#include "collectiondatatypemodel.h" -#include "model.h" -#include "nodemetainfo.h" -#include "propertymetainfo.h" - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include - -using DataType = QmlDesigner::CollectionDetails::DataType; - -namespace { - -using CollectionDataVariant = std::variant; - -inline bool operator<(const QColor &a, const QColor &b) -{ - return a.name(QColor::HexArgb) < b.name(QColor::HexArgb); -} - -inline CollectionDataVariant valueToVariant(const QVariant &value, DataType type) -{ - switch (type) { - case DataType::String: - return value.toString(); - case DataType::Real: - return value.toDouble(); - case DataType::Integer: - return value.toInt(); - case DataType::Boolean: - return value.toBool(); - case DataType::Color: - return value.value(); - case DataType::Image: - case DataType::Url: - return value.value(); - default: - return false; - } -} - -struct LessThanVisitor -{ - template - bool operator()(const T1 &a, const T2 &b) const - { - return CollectionDataVariant(a).index() < CollectionDataVariant(b).index(); - } - - template - bool operator()(const T &a, const T &b) const - { - return a < b; - } -}; - -Utils::FilePath findFile(const Utils::FilePath &path, const QString &fileName) -{ - QDirIterator it(path.toString(), QDirIterator::Subdirectories); - - while (it.hasNext()) { - QFileInfo file(it.next()); - if (file.isDir()) - continue; - - if (file.fileName() == fileName) - return Utils::FilePath::fromFileInfo(file); - } - return {}; -} - -Utils::FilePath dataStoreDir() -{ - using Utils::FilePath; - ProjectExplorer::Project *currentProject = ProjectExplorer::ProjectManager::startupProject(); - - if (!currentProject) - return {}; - - FilePath oldImportDirectory = currentProject->projectDirectory().pathAppended( - "imports/" + currentProject->displayName()); - if (oldImportDirectory.exists()) - return oldImportDirectory; - - return currentProject->projectDirectory().pathAppended(currentProject->displayName()); -} - -inline Utils::FilePath collectionPath(const QString &filePath) -{ - return dataStoreDir().pathAppended(filePath); -} - -inline Utils::FilePath qmlDirFilePath() -{ - return collectionPath("qmldir"); -} - -} // namespace - -namespace QmlDesigner::CollectionEditorUtils { - -bool variantIslessThan(const QVariant &a, const QVariant &b, DataType type) -{ - return std::visit(LessThanVisitor{}, valueToVariant(a, type), valueToVariant(b, type)); -} - -QString getSourceCollectionType(const ModelNode &node) -{ - using namespace QmlDesigner; - if (node.type() == CollectionEditorConstants::JSONCOLLECTIONMODEL_TYPENAME) - return "json"; - - return {}; -} - -Utils::FilePath dataStoreJsonFilePath() -{ - return collectionPath("models.json"); -} - -Utils::FilePath dataStoreQmlFilePath() -{ - return collectionPath("DataStore.qml"); -} - -bool canAcceptCollectionAsModel(const ModelNode &node) -{ - const NodeMetaInfo nodeMetaInfo = node.metaInfo(); - if (!nodeMetaInfo.isValid()) - return false; - - const PropertyMetaInfo modelProperty = nodeMetaInfo.property("model"); - if (!modelProperty.isValid()) - return false; - - return modelProperty.isWritable() && !modelProperty.isPrivate() - && modelProperty.propertyType().isVariant(); -} - -bool hasTextRoleProperty(const ModelNode &node) -{ - const NodeMetaInfo nodeMetaInfo = node.metaInfo(); - if (!nodeMetaInfo.isValid()) - return false; - - const PropertyMetaInfo textRoleProperty = nodeMetaInfo.property("textRole"); - if (!textRoleProperty.isValid()) - return false; - - return textRoleProperty.isWritable() && !textRoleProperty.isPrivate() - && textRoleProperty.propertyType().isString(); -} - -QString getSourceCollectionPath(const ModelNode &dataStoreNode) -{ - using Utils::FilePath; - if (!dataStoreNode.isValid()) - return {}; - - const FilePath expectedFile = dataStoreJsonFilePath(); - - if (expectedFile.exists()) - return expectedFile.toFSPathString(); - - return {}; -} - -bool isDataStoreNode(const ModelNode &dataStoreNode) -{ - using Utils::FilePath; - - if (!dataStoreNode.isValid()) - return false; - - const FilePath expectedFile = dataStoreQmlFilePath(); - - if (!expectedFile.exists()) - return false; - - FilePath modelPath = FilePath::fromUserInput(dataStoreNode.model()->fileUrl().toLocalFile()); - - return modelPath.isSameFile(expectedFile); -} - -bool ensureDataStoreExists(bool &justCreated) -{ - using Utils::FilePath; - using Utils::FileReader; - using Utils::FileSaver; - - FilePath qmlDestinationPath = dataStoreQmlFilePath(); - justCreated = false; - - auto extractDependency = [&justCreated](const FilePath &filePath) -> bool { - if (filePath.exists()) - return true; - - const QString templateFileName = filePath.fileName() + u".tpl"; - const FilePath templatePath = findFile(Core::ICore::resourcePath(), templateFileName); - if (!templatePath.exists()) { - qWarning() << Q_FUNC_INFO << __LINE__ << templateFileName << "does not exist"; - return false; - } - - if (!filePath.parentDir().ensureWritableDir()) { - qWarning() << Q_FUNC_INFO << __LINE__ << "Cannot create directory" - << filePath.parentDir(); - return false; - } - - if (templatePath.copyFile(filePath)) { - justCreated = true; - return true; - } - - qWarning() << Q_FUNC_INFO << __LINE__ << "Cannot copy" << templateFileName << "to" << filePath; - return false; - }; - - if (!extractDependency(dataStoreJsonFilePath())) - return false; - - if (!extractDependency(collectionPath("data.json"))) - return false; - - if (!extractDependency(collectionPath("JsonData.qml"))) - return false; - - if (!qmlDestinationPath.exists()) { - if (qmlDestinationPath.ensureExistingFile()) { - justCreated = true; - } else { - qWarning() << Q_FUNC_INFO << __LINE__ << "Can't create DataStore Qml File"; - return false; - } - } - - FilePath qmlDirPath = qmlDirFilePath(); - qmlDirPath.ensureExistingFile(); - - FileReader qmlDirReader; - if (!qmlDirReader.fetch(qmlDirPath)) { - qWarning() << Q_FUNC_INFO << __LINE__ << "Can't read the content of the qmldir"; - return false; - } - - QByteArray qmlDirContent = qmlDirReader.data(); - const QList qmlDirLines = qmlDirContent.split('\n'); - for (const QByteArray &line : qmlDirLines) { - if (line.startsWith("singleton DataStore ")) - return true; - } - - if (!qmlDirContent.isEmpty() && qmlDirContent.back() != '\n') - qmlDirContent.append("\n"); - qmlDirContent.append("singleton DataStore 1.0 DataStore.qml\n"); - - FileSaver qmlDirSaver(qmlDirPath); - qmlDirSaver.write(qmlDirContent); - - if (qmlDirSaver.finalize()) { - justCreated = true; - return true; - } - - qWarning() << Q_FUNC_INFO << __LINE__ << "Can't write to the qmldir file"; - return false; -} - -QJsonObject defaultCollection() -{ - QJsonObject collectionObject; - - QJsonArray columns; - QJsonObject defaultColumn; - defaultColumn.insert("name", "Column 1"); - defaultColumn.insert("type", CollectionDataTypeModel::dataTypeToString(DataType::String)); - columns.append(defaultColumn); - - QJsonArray collectionData; - QJsonArray cellData; - cellData.append(QString{}); - collectionData.append(cellData); - - collectionObject.insert("columns", columns); - collectionObject.insert("data", collectionData); - - 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(); -} - -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 deleted file mode 100644 index 355addf59be..00000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h +++ /dev/null @@ -1,46 +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 - -#pragma once - -#include "collectiondetails.h" -#include "collectioneditorconstants.h" - -QT_BEGIN_NAMESPACE -class QJsonArray; -class QJsonObject; -QT_END_NAMESPACE - -namespace Utils { -class FilePath; -} - -namespace QmlDesigner::CollectionEditorUtils { - -bool variantIslessThan(const QVariant &a, const QVariant &b, CollectionDetails::DataType type); - -QString getSourceCollectionType(const QmlDesigner::ModelNode &node); - -QString getSourceCollectionPath(const QmlDesigner::ModelNode &dataStoreNode); - -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); - -bool canAcceptCollectionAsModel(const ModelNode &node); - -bool hasTextRoleProperty(const ModelNode &node); - -QJsonObject defaultCollection(); - -QJsonObject defaultColorCollection(); - -} // namespace QmlDesigner::CollectionEditorUtils diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.cpp deleted file mode 100644 index d27a077d2a7..00000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.cpp +++ /dev/null @@ -1,521 +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 "collectionlistmodel.h" - -#include "collectioneditorutils.h" - -#include -#include -#include - -#include -#include -#include - -namespace { - -template -bool containsItem(const std::initializer_list &container, const ValueType &value) -{ - auto begin = std::cbegin(container); - auto end = std::cend(container); - - auto it = std::find(begin, end, 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() - : QAbstractListModel() -{ - connect(this, &CollectionListModel::modelReset, this, &CollectionListModel::updateEmpty); - connect(this, &CollectionListModel::rowsRemoved, this, &CollectionListModel::updateEmpty); - connect(this, &CollectionListModel::rowsInserted, this, &CollectionListModel::updateEmpty); -} - -QHash CollectionListModel::roleNames() const -{ - static QHash roles; - if (roles.isEmpty()) { - roles.insert(Super::roleNames()); - roles.insert({ - {IdRole, "collectionId"}, - {NameRole, "collectionName"}, - {SelectedRole, "collectionIsSelected"}, - }); - } - 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()) - return false; - - if (containsItem({Qt::EditRole, Qt::DisplayRole, NameRole}, role)) { - if (collectionExists(value.toString())) - return false; - - QString oldName = collectionNameAt(index.row()); - bool nameChanged = value != data(index); - if (nameChanged) { - QString newName = value.toString(); - 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; - } - } - } else if (role == SelectedRole) { - if (value.toBool() != index.data(SelectedRole).toBool()) { - setSelectedIndex(value.toBool() ? index.row() : -1); - return true; - } - } - return false; -} - -bool CollectionListModel::removeRows(int row, int count, const QModelIndex &parent) -{ - const int rows = rowCount(parent); - if (row >= rows) - return false; - - row = qBound(0, row, rows - 1); - count = qBound(0, count, rows - row); - - 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(); - - 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); - } - - updateSelectedCollectionName(); - return true; - } else { - emit warning("Remove Model", errorString); - return false; - } -} - -QVariant CollectionListModel::data(const QModelIndex &index, int role) const -{ - QTC_ASSERT(index.isValid(), return {}); - - switch (role) { - case IdRole: - return index.row(); - case SelectedRole: - return index.row() == m_selectedIndex; - case NameRole: - default: - return m_data.at(index.row()); - } -} - -void CollectionListModel::setDataStoreNode(const ModelNode &dataStoreNode) -{ - m_dataStoreNode = dataStoreNode; - update(); -} - -int CollectionListModel::selectedIndex() const -{ - return m_selectedIndex; -} - -ModelNode CollectionListModel::sourceNode() const -{ - return m_dataStoreNode; -} - -bool CollectionListModel::collectionExists(const QString &collectionName) const -{ - return m_data.contains(collectionName); -} - -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(); - 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 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 -{ - return index(idx).data(NameRole).toString(); -} - -QString CollectionListModel::selectedCollectionName() const -{ - return m_selectedCollectionName; -} - -void CollectionListModel::update() -{ - using Utils::FilePath; - using Utils::FileReader; - - 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) -{ - idx = (idx > -1 && idx < rowCount()) ? 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); - 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.")); - } -} - -void CollectionListModel::updateEmpty() -{ - bool isEmptyNow = m_data.isEmpty(); - if (m_isEmpty != isEmptyNow) { - m_isEmpty = isEmptyNow; - emit isEmptyChanged(m_isEmpty); - - if (m_isEmpty) - setSelectedIndex(-1); - } -} - -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 deleted file mode 100644 index 7902fd59097..00000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.h +++ /dev/null @@ -1,79 +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 - -#pragma once - -#include -#include - -#include "modelnode.h" - -namespace QmlDesigner { - -class CollectionListModel : public QAbstractListModel -{ - Q_OBJECT - - Q_PROPERTY(int selectedIndex MEMBER m_selectedIndex NOTIFY selectedIndexChanged) - Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged) - Q_PROPERTY(QString selectedCollectionName - READ selectedCollectionName - WRITE selectCollectionName - NOTIFY selectedCollectionNameChanged) - -public: - enum Roles { IdRole = Qt::UserRole + 1, NameRole, SelectedRole }; - - 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 setDataStoreNode(const ModelNode &dataStoreNode = {}); - - Q_INVOKABLE int selectedIndex() const; - Q_INVOKABLE ModelNode sourceNode() 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(QString collectionName, bool selectAtLeastOne = false); - QString collectionNameAt(int idx) const; - 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; - ModelNode m_dataStoreNode; - QString m_selectedCollectionName; - QStringList m_data; -}; - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp deleted file mode 100644 index 0c9a2eed94d..00000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp +++ /dev/null @@ -1,501 +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 "collectionview.h" - -#include "collectiondatatypemodel.h" -#include "collectiondetailsmodel.h" -#include "collectioneditorconstants.h" -#include "collectioneditorutils.h" -#include "collectionlistmodel.h" -#include "collectionwidget.h" -#include "datastoremodelnode.h" -#include "designmodecontext.h" -#include "nodeabstractproperty.h" -#include "nodemetainfo.h" -#include "nodeproperty.h" -#include "qmldesignerplugin.h" -#include "variantproperty.h" - -#include -#include -#include -#include - -#include -#include -#include - -#include - -namespace { - -bool isStudioCollectionModel(const QmlDesigner::ModelNode &node) -{ - return node.metaInfo().isQtQuickStudioUtilsJsonListModel(); -} - -inline bool isProjectImport(const QmlDesigner::Import &import) -{ - ProjectExplorer::Project *currentProject = ProjectExplorer::ProjectManager::startupProject(); - return currentProject && import.toString() == currentProject->displayName(); -} - -inline void setVariantPropertyValue(const QmlDesigner::ModelNode &node, - const QmlDesigner::PropertyName &propertyName, - const QVariant &value) -{ - 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 { - -CollectionView::CollectionView(ExternalDependenciesInterface &externalDependencies) - : AbstractView(externalDependencies) - , m_dataStore(std::make_unique()) - -{ -} - -CollectionView::~CollectionView() = default; - -bool CollectionView::hasWidget() const -{ - return true; -} - -QmlDesigner::WidgetInfo CollectionView::widgetInfo() -{ - if (!m_widget) { - m_widget = Utils::makeUniqueObjectPtr(this); - m_widget->setMinimumSize(m_widget->minimumSizeHint()); - connect(ProjectExplorer::ProjectManager::instance(), - &ProjectExplorer::ProjectManager::startupProjectChanged, m_widget.get(), [&] { - resetDataStoreNode(); - m_widget->collectionDetailsModel()->removeAllCollections(); - }); - - auto collectionEditorContext = new Internal::CollectionEditorContext(m_widget.get()); - Core::ICore::addContextObject(collectionEditorContext); - CollectionListModel *listModel = m_widget->listModel().data(); - - connect(listModel, - &CollectionListModel::selectedCollectionNameChanged, - this, - [this](const QString &collection) { - m_widget->collectionDetailsModel()->loadCollection(dataStoreNode(), collection); - }); - - connect(listModel, &CollectionListModel::isEmptyChanged, this, [this](bool isEmpty) { - if (isEmpty) - m_widget->collectionDetailsModel()->loadCollection({}, {}); - }); - - connect(listModel, &CollectionListModel::modelReset, this, [this] { - CollectionListModel *listModel = m_widget->listModel().data(); - if (listModel->sourceNode() == dataStoreNode()) - m_dataStore->setCollectionNames(listModel->collections()); - }); - - 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); - m_widget->collectionDetailsModel()->renameCollection(dataStoreNode(), - oldName, - newName); - }); - - connect(listModel, - &CollectionListModel::collectionsRemoved, - this, - [this](const QStringList &collectionNames) { - m_dataStore->removeCollections(collectionNames); - for (const QString &collectionName : collectionNames) { - m_widget->collectionDetailsModel()->removeCollection(dataStoreNode(), - collectionName); - } - }); - } - - return createWidgetInfo(m_widget.get(), - "CollectionEditor", - WidgetInfo::LeftPane, - 0, - tr("Model Editor [beta]"), - tr("Model Editor view")); -} - -void CollectionView::modelAttached(Model *model) -{ - AbstractView::modelAttached(model); - m_widget->setProjectImportExists(Utils::anyOf(model->imports(), isProjectImport)); - resetDataStoreNode(); -} - -void CollectionView::modelAboutToBeDetached([[maybe_unused]] Model *model) -{ - unloadDataStore(); - m_widget->setProjectImportExists(false); -} - -void CollectionView::selectedNodesChanged(const QList &selectedNodeList, - [[maybe_unused]] const QList &lastSelectedNodeList) -{ - if (!m_widget) - return; - - QList selectedCollectionNodes = Utils::filtered(selectedNodeList, - &isStudioCollectionModel); - - bool singleNonCollectionNodeSelected = selectedNodeList.size() == 1 - && selectedCollectionNodes.isEmpty(); - - bool singleSelectedHasModelProperty = false; - if (singleNonCollectionNodeSelected) { - const ModelNode selectedNode = selectedNodeList.first(); - singleSelectedHasModelProperty = CollectionEditorUtils::canAcceptCollectionAsModel( - selectedNode); - } - - m_widget->setTargetNodeSelected(singleSelectedHasModelProperty); -} - -void CollectionView::importsChanged(const Imports &addedImports, const Imports &removedImports) -{ - if (Utils::anyOf(addedImports, isProjectImport)) { - m_widget->setProjectImportExists(true); - resetDataStoreNode(); - } else if (Utils::anyOf(removedImports, isProjectImport)) { - m_widget->setProjectImportExists(false); - unloadDataStore(); - } -} - -void CollectionView::customNotification(const AbstractView *, - const QString &identifier, - const QList &nodeList, - const QList &data) -{ - if (!m_widget) - return; - - if (identifier == QLatin1String("item_library_created_by_drop") && !nodeList.isEmpty()) - onItemLibraryNodeCreated(nodeList.first()); - else if (identifier == QLatin1String("open_collection_by_id") && !data.isEmpty()) - 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) -{ - executeInTransaction(Q_FUNC_INFO, [this, &url, &name]() { - ensureStudioModelImport(); - QString sourceAddress; - if (url.isLocalFile()) { - Utils::FilePath fp = QmlDesignerPlugin::instance()->currentDesignDocument()->fileName().parentDir(); - sourceAddress = Utils::FilePath::calcRelativePath(url.toLocalFile(), - fp.absoluteFilePath().toString()); - } 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"); - sourceProperty.setValue(sourceAddress); - nameProperty.setValue(name); - resourceNode.setIdWithoutRefactoring(model()->generateIdFromName(name, "model")); - rootModelNode().defaultNodeAbstractProperty().reparentHere(resourceNode); - }); -} - -void CollectionView::addProjectImport() -{ - if (!m_widget) - return; - - ProjectExplorer::Project *currentProject = ProjectExplorer::ProjectManager::startupProject(); - if (!currentProject) - return; - - executeInTransaction(__FUNCTION__, [&] { - Import import = Import::createLibraryImport(currentProject->displayName()); - if (!model()->hasImport(import, true, true)) - model()->changeImports({import}, {}); - }); -} - -void CollectionView::assignCollectionToNode(const QString &collectionName, const ModelNode &node) -{ - if (!m_widget) - return; - - using DataType = CollectionDetails::DataType; - executeInTransaction("CollectionView::assignCollectionToNode", [&]() { - m_dataStore->assignCollectionToNode( - 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"); - // Remove the old model node if is available - if (delegateProperty.modelNode()) - delegateProperty.modelNode().destroy(); - - delegateProperty.setModelNode(rowItem); - } - }); -} - -void CollectionView::assignCollectionToSelectedNode(const QString &collectionName) -{ - QTC_ASSERT(dataStoreNode() && hasSingleSelectedModelNode(), return); - assignCollectionToNode(collectionName, singleSelectedModelNode()); -} - -void CollectionView::addNewCollection(const QString &collectionName, const QJsonObject &localCollection) -{ - if (!m_widget) - return; - - addTask(QSharedPointer( - new AddCollectionTask(this, m_widget->listModel(), localCollection, collectionName))); -} - -void CollectionView::openCollection(const QString &collectionName) -{ - if (!m_widget) - return; - - m_widget->openCollection(collectionName); -} - -void CollectionView::registerDeclarativeType() -{ - CollectionDetails::registerDeclarativeType(); - CollectionDataTypeModel::registerDeclarativeType(); -} - -void CollectionView::resetDataStoreNode() -{ - if (!m_widget) - return; - - m_dataStore->reloadModel(); - - ModelNode dataStore = dataStoreNode(); - m_widget->setDataStoreExists(dataStore.isValid()); - if (!dataStore || m_widget->listModel()->sourceNode() == dataStore) - return; - - bool dataStoreSingletonFound = m_dataStoreTypeFound; - if (!dataStoreSingletonFound && rewriterView() && rewriterView()->isAttached()) { - const QList types = rewriterView()->getQMLTypes(); - for (const QmlTypeData &cppTypeData : types) { - if (cppTypeData.isSingleton && cppTypeData.typeName == "DataStore") { - dataStoreSingletonFound = true; - break; - } - } - if (!dataStoreSingletonFound && !m_rewriterAmended) { - rewriterView()->forceAmend(); - m_rewriterAmended = true; - } - } - - if (dataStoreSingletonFound) { - m_widget->listModel()->setDataStoreNode(dataStore); - m_dataStoreTypeFound = true; - - while (!m_delayedTasks.isEmpty()) - m_delayedTasks.takeFirst()->process(); - } else if (++m_reloadCounter < 50) { - QTimer::singleShot(200, this, &CollectionView::resetDataStoreNode); - } else { - QTC_ASSERT(false, m_delayedTasks.clear()); - } -} - -ModelNode CollectionView::dataStoreNode() const -{ - return m_dataStore->modelNode(); -} - -void CollectionView::ensureDataStoreExists() -{ - bool filesJustCreated = false; - bool filesExist = CollectionEditorUtils::ensureDataStoreExists(filesJustCreated); - if (filesExist && filesJustCreated) { - // Force code model reset to notice changes to existing module - if (auto modelManager = QmlJS::ModelManagerInterface::instance()) - modelManager->resetCodeModel(); - resetDataStoreNode(); - } -} - -QString CollectionView::collectionNameFromDataStoreChildren(const PropertyName &childPropertyName) const -{ - return dataStoreNode() - .nodeProperty(childPropertyName) - .modelNode() - .property(CollectionEditorConstants::JSONCHILDMODELNAME_PROPERTY) - .toVariantProperty() - .value() - .toString(); -} - -NodeMetaInfo CollectionView::jsonCollectionMetaInfo() const -{ - return model()->metaInfo(CollectionEditorConstants::JSONCOLLECTIONMODEL_TYPENAME); -} - -void CollectionView::unloadDataStore() -{ - m_reloadCounter = 0; - m_rewriterAmended = false; - m_dataStoreTypeFound = false; - QTC_ASSERT(m_delayedTasks.isEmpty(), m_delayedTasks.clear()); - if (m_widget) { - m_widget->setDataStoreExists(dataStoreNode().isValid()); - m_widget->listModel()->setDataStoreNode(); - } -} - -void CollectionView::ensureStudioModelImport() -{ - executeInTransaction(__FUNCTION__, [&] { - Import import = Import::createLibraryImport(CollectionEditorConstants::COLLECTIONMODEL_IMPORT); - try { - if (!model()->hasImport(import, true, true)) - model()->changeImports({import}, {}); - } catch (const Exception &) { - QTC_ASSERT(false, return); - } - }); -} - -void CollectionView::onItemLibraryNodeCreated(const ModelNode &node) -{ - if (!m_widget) - return; - - if (node.metaInfo().isQtQuickListView()) { - addTask(QSharedPointer( - new DropListViewTask(this, m_widget->listModel(), node))); - } -} - -void CollectionView::addTask(QSharedPointer task) -{ - ensureDataStoreExists(); - if (m_dataStoreTypeFound) - task->process(); - else if (dataStoreNode()) - m_delayedTasks << task; -} - -CollectionTask::CollectionTask(CollectionView *view, CollectionListModel *listModel) - : m_collectionView(view) - , m_listModel(listModel) -{} - -DropListViewTask::DropListViewTask(CollectionView *view, - CollectionListModel *listModel, - const ModelNode &node) - : CollectionTask(view, listModel) - , m_node(node) -{} - -void DropListViewTask::process() -{ - AbstractView *view = m_node.view(); - if (!m_node || !m_collectionView || !m_listModel || !view) - return; - - 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, - CollectionListModel *listModel, - const QJsonObject &localJsonObject, - const QString &collectionName) - : CollectionTask(view, listModel) - , m_localJsonObject(localJsonObject) - , m_name(collectionName) -{} - -void AddCollectionTask::process() -{ - if (!m_listModel) - return; - - const QString newCollectionName = m_listModel->collectionExists(m_name) - ? m_listModel->getUniqueCollectionName(m_name) - : m_name; - - 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 deleted file mode 100644 index 3de3bd7ae6d..00000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionview.h +++ /dev/null @@ -1,124 +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 - -#pragma once - -#include "datastoremodelnode.h" - -#include -#include - -#include - -#include - -namespace QmlJS { -class Document; -} - -namespace QmlDesigner { - -class CollectionDetails; -class CollectionListModel; -class CollectionTask; -class CollectionWidget; -class DataStoreModelNode; - -class CollectionView : public AbstractView -{ - Q_OBJECT - -public: - explicit CollectionView(ExternalDependenciesInterface &externalDependencies); - ~CollectionView(); - - bool hasWidget() const override; - WidgetInfo widgetInfo() override; - - void modelAttached(Model *model) override; - void modelAboutToBeDetached(Model *model) override; - - void selectedNodesChanged(const QList &selectedNodeList, - const QList &lastSelectedNodeList) override; - - void importsChanged(const Imports &addedImports, const Imports &removedImports) override; - - void customNotification(const AbstractView *view, - const QString &identifier, - const QList &nodeList, - const QList &data) override; - - void addResource(const QUrl &url, const QString &name); - - void addProjectImport(); - void assignCollectionToNode(const QString &collectionName, const ModelNode &node); - void assignCollectionToSelectedNode(const QString &collectionName); - void addNewCollection(const QString &collectionName, const QJsonObject &localCollection); - - void openCollection(const QString &collectionName); - - static void registerDeclarativeType(); - - void resetDataStoreNode(); - ModelNode dataStoreNode() const; - void ensureDataStoreExists(); - QString collectionNameFromDataStoreChildren(const PropertyName &childPropertyName) const; - -private: - friend class CollectionTask; - - NodeMetaInfo jsonCollectionMetaInfo() const; - void unloadDataStore(); - void ensureStudioModelImport(); - void onItemLibraryNodeCreated(const ModelNode &node); - void addTask(QSharedPointer task); - - std::unique_ptr m_dataStore; - Utils::UniqueObjectPtr m_widget; - QList> m_delayedTasks; - bool m_dataStoreTypeFound = false; - bool m_rewriterAmended = false; - int m_reloadCounter = 0; -}; - -class CollectionTask -{ -public: - CollectionTask(CollectionView *view, CollectionListModel *listModel); - CollectionTask() = delete; - virtual ~CollectionTask() = default; - - virtual void process() = 0; - -protected: - QPointer m_collectionView; - QPointer m_listModel; -}; - -class DropListViewTask : public CollectionTask -{ -public: - DropListViewTask(CollectionView *view, CollectionListModel *listModel, const ModelNode &node); - - void process() override; - -private: - ModelNode m_node; -}; - -class AddCollectionTask : public CollectionTask -{ -public: - AddCollectionTask(CollectionView *view, - CollectionListModel *listModel, - const QJsonObject &localJsonObject, - const QString &collectionName); - - void process() override; - -private: - QJsonObject m_localJsonObject; - QString m_name; -}; - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp deleted file mode 100644 index dd706145cf2..00000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp +++ /dev/null @@ -1,320 +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 "collectionwidget.h" - -#include "collectiondetails.h" -#include "collectiondetailsmodel.h" -#include "collectiondetailssortfiltermodel.h" -#include "collectioneditorutils.h" -#include "collectionlistmodel.h" -#include "collectionview.h" -#include "designmodewidget.h" -#include "qmldesignerconstants.h" -#include "qmldesignerplugin.h" -#include "theme.h" - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace { - -QString collectionViewResourcesPath() -{ -#ifdef SHARE_QML_PATH - if (qEnvironmentVariableIsSet("LOAD_QML_FROM_SOURCE")) - return QLatin1String(SHARE_QML_PATH) + "/collectionEditorQmlSource"; -#endif - return Core::ICore::resourcePath("qmldesigner/collectionEditorQmlSource").toString(); -} - -QString getPreferredCollectionName(const QUrl &url, const QString &collectionName) -{ - if (collectionName.isEmpty()) { - QFileInfo fileInfo(url.isLocalFile() ? url.toLocalFile() : url.toString()); - return fileInfo.completeBaseName(); - } - - return collectionName; -} - -} // namespace - -namespace QmlDesigner { -CollectionWidget::CollectionWidget(CollectionView *view) - : m_view(view) - , m_listModel(new CollectionListModel) - , m_collectionDetailsModel(new CollectionDetailsModel) - , m_collectionDetailsSortFilterModel(std::make_unique()) - , m_quickWidget(new StudioQuickWidget(this)) -{ - setWindowTitle(tr("Model Editor", "Title of model editor widget")); - - Core::IContext *icontext = nullptr; - Core::Context context(Constants::C_QMLCOLLECTIONEDITOR); - icontext = new Core::IContext(this); - icontext->setContext(context); - icontext->setWidget(this); - - connect(m_listModel, &CollectionListModel::warning, this, &CollectionWidget::warn); - - m_collectionDetailsSortFilterModel->setSourceModel(m_collectionDetailsModel); - - m_quickWidget->quickWidget()->setObjectName(Constants::OBJECT_NAME_COLLECTION_EDITOR); - m_quickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView); - m_quickWidget->engine()->addImportPath(collectionViewResourcesPath() + "/imports"); - m_quickWidget->setClearColor(Theme::getColor(Theme::Color::DSpanelBackground)); - - Theme::setupTheme(m_quickWidget->engine()); - m_quickWidget->quickWidget()->installEventFilter(this); - - auto layout = new QVBoxLayout(this); - layout->setContentsMargins({}); - layout->setSpacing(0); - layout->addWidget(m_quickWidget.data()); - - qmlRegisterAnonymousType("CollectionEditorBackend", 1); - auto map = m_quickWidget->registerPropertyMap("CollectionEditorBackend"); - map->setProperties({ - {"rootView", QVariant::fromValue(this)}, - {"model", QVariant::fromValue(m_listModel.data())}, - {"collectionDetailsModel", QVariant::fromValue(m_collectionDetailsModel.data())}, - {"collectionDetailsSortFilterModel", - QVariant::fromValue(m_collectionDetailsSortFilterModel.get())}, - }); - - auto hotReloadShortcut = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_F4), this); - connect(hotReloadShortcut, &QShortcut::activated, this, &CollectionWidget::reloadQmlSource); - - reloadQmlSource(); - - QmlDesignerPlugin::trackWidgetFocusTime(this, Constants::EVENT_MODELEDITOR_TIME); -} - -CollectionWidget::~CollectionWidget() = default; - -void CollectionWidget::contextHelp(const Core::IContext::HelpCallback &callback) const -{ - if (m_view) - QmlDesignerPlugin::contextHelp(callback, m_view->contextHelpId()); - else - callback({}); -} - -QPointer CollectionWidget::listModel() const -{ - return m_listModel; -} - -QPointer CollectionWidget::collectionDetailsModel() const -{ - return m_collectionDetailsModel; -} - -void CollectionWidget::reloadQmlSource() -{ - const QString collectionViewQmlPath = collectionViewResourcesPath() + "/CollectionView.qml"; - - QTC_ASSERT(QFileInfo::exists(collectionViewQmlPath), return); - - m_quickWidget->setSource(QUrl::fromLocalFile(collectionViewQmlPath)); - - if (!m_quickWidget->rootObject()) { - QString errorString; - const auto errors = m_quickWidget->errors(); - for (const QQmlError &error : errors) - errorString.append("\n" + error.toString()); - - Core::AsynchronousMessageBox::warning(tr("Cannot Create QtQuick View"), - tr("StatesEditorWidget: %1 cannot be created.%2") - .arg(collectionViewQmlPath, errorString)); - return; - } -} - -QSize CollectionWidget::minimumSizeHint() const -{ - return {300, 300}; -} - -bool CollectionWidget::loadJsonFile(const QUrl &url, const QString &collectionName) -{ - if (!isJsonFile(url)) - return false; - - 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)); - - return true; -} - -bool CollectionWidget::isJsonFile(const QUrl &url) const -{ - 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.data(), &error); - if (error.error) - return false; - - return true; -} - -bool CollectionWidget::isCsvFile(const QUrl &url) const -{ - QString filePath = url.isLocalFile() ? url.toLocalFile() : url.toString(); - QFileInfo fileInfo(filePath); - return fileInfo.exists() && !fileInfo.suffix().compare("csv", Qt::CaseInsensitive); -} - -bool CollectionWidget::isValidUrlToImport(const QUrl &url) const -{ - using Utils::FilePath; - FilePath fileInfo = FilePath::fromUserInput(url.isLocalFile() ? url.toLocalFile() - : url.toString()); - if (fileInfo.suffix() == "json") - return isJsonFile(url); - - if (fileInfo.suffix() == "csv") - return isCsvFile(url); - - return false; -} - -bool CollectionWidget::importFile(const QString &collectionName, - const QUrl &url, - const bool &firstRowIsHeader) -{ - using Utils::FilePath; - - FilePath fileInfo = FilePath::fromUserInput(url.isLocalFile() ? url.toLocalFile() - : url.toString()); - CollectionDetails loadedCollection; - QByteArray fileContent; - - auto loadUrlContent = [&]() -> bool { - Utils::FileReader file; - if (file.fetch(fileInfo)) { - fileContent = file.data(); - return true; - } - - warn(tr("Import from file"), tr("Cannot import from file \"%1\"").arg(fileInfo.fileName())); - return false; - }; - - if (fileInfo.suffix() == "json") { - if (!loadUrlContent()) - return false; - - QJsonParseError 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())); - } - } else if (fileInfo.suffix() == "csv") { - if (!loadUrlContent()) - return false; - loadedCollection = CollectionDetails::fromImportedCsv(fileContent, firstRowIsHeader); - } - - if (loadedCollection.columns()) { - m_view->addNewCollection(collectionName, loadedCollection.toLocalJson()); - return true; - } else { - warn(tr("Can not add a model to the JSON file"), - tr("The imported model is empty or is not supported.")); - } - return false; -} - -void CollectionWidget::addProjectImport() -{ - m_view->addProjectImport(); -} - -void CollectionWidget::addCollectionToDataStore(const QString &collectionName) -{ - m_view->addNewCollection(collectionName, CollectionEditorUtils::defaultCollection()); -} - -void CollectionWidget::assignCollectionToSelectedNode(const QString collectionName) -{ - m_view->assignCollectionToSelectedNode(collectionName); -} - -void CollectionWidget::openCollection(const QString &collectionName) -{ - m_listModel->selectCollectionName(collectionName); - QmlDesignerPlugin::instance()->mainWidget()->showDockWidget("CollectionEditor", true); -} - -ModelNode CollectionWidget::dataStoreNode() const -{ - return m_view->dataStoreNode(); -} - -void CollectionWidget::warn(const QString &title, const QString &body) -{ - QMetaObject::invokeMethod(m_quickWidget->rootObject(), - "showWarning", - Q_ARG(QVariant, title), - Q_ARG(QVariant, body)); -} - -void CollectionWidget::setTargetNodeSelected(bool selected) -{ - if (m_targetNodeSelected == selected) - return; - - m_targetNodeSelected = selected; - emit targetNodeSelectedChanged(m_targetNodeSelected); -} - -void CollectionWidget::setProjectImportExists(bool exists) -{ - if (m_projectImportExists == exists) - return; - - m_projectImportExists = exists; - emit projectImportExistsChanged(m_projectImportExists); -} - -void CollectionWidget::setDataStoreExists(bool exists) -{ - if (m_dataStoreExists == exists) - return; - - m_dataStoreExists = exists; - emit dataStoreExistsChanged(m_dataStoreExists); -} - -void CollectionWidget::deleteSelectedCollection() -{ - QMetaObject::invokeMethod(m_quickWidget->quickWidget()->rootObject(), "deleteSelectedCollection"); -} - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h deleted file mode 100644 index 13c3566c78e..00000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h +++ /dev/null @@ -1,81 +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 - -#pragma once - -#include - -#include - -class StudioQuickWidget; - -namespace QmlDesigner { - -class CollectionDetailsModel; -class CollectionDetailsSortFilterModel; -class CollectionListModel; -class CollectionView; -class ModelNode; - -class CollectionWidget : public QFrame -{ - Q_OBJECT - - Q_PROPERTY(bool targetNodeSelected MEMBER m_targetNodeSelected NOTIFY targetNodeSelectedChanged) - Q_PROPERTY(bool projectImportExists MEMBER m_projectImportExists NOTIFY projectImportExistsChanged) - Q_PROPERTY(bool dataStoreExists MEMBER m_dataStoreExists NOTIFY dataStoreExistsChanged) - -public: - CollectionWidget(CollectionView *view); - ~CollectionWidget(); - void contextHelp(const Core::IContext::HelpCallback &callback) const; - - QPointer listModel() const; - QPointer collectionDetailsModel() const; - - void reloadQmlSource(); - - QSize minimumSizeHint() const override; - - Q_INVOKABLE bool loadJsonFile(const QUrl &url, const QString &collectionName = {}); - Q_INVOKABLE bool loadCsvFile(const QUrl &url, const QString &collectionName = {}); - 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 importFile(const QString &collectionName, - const QUrl &url, - const bool &firstRowIsHeader = true); - - Q_INVOKABLE void addProjectImport(); - Q_INVOKABLE void addCollectionToDataStore(const QString &collectionName); - Q_INVOKABLE void assignCollectionToSelectedNode(const QString collectionName); - Q_INVOKABLE void openCollection(const QString &collectionName); - Q_INVOKABLE ModelNode dataStoreNode() const; - - void warn(const QString &title, const QString &body); - void setTargetNodeSelected(bool selected); - void setProjectImportExists(bool exists); - void setDataStoreExists(bool exists); - - void deleteSelectedCollection(); - -signals: - void targetNodeSelectedChanged(bool); - void projectImportExistsChanged(bool); - void dataStoreExistsChanged(bool); - -private: - QString generateUniqueCollectionName(const ModelNode &node, const QString &name); - - QPointer m_view; - QPointer m_listModel; - QPointer m_collectionDetailsModel; - std::unique_ptr m_collectionDetailsSortFilterModel; - QScopedPointer m_quickWidget; - bool m_targetNodeSelected = false; - bool m_projectImportExists = false; - bool m_dataStoreExists = false; -}; - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.cpp b/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.cpp deleted file mode 100644 index 0d7b0976e50..00000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.cpp +++ /dev/null @@ -1,511 +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 "datastoremodelnode.h" - -#include "abstractview.h" -#include "collectioneditorconstants.h" -#include "collectioneditorutils.h" -#include "model/qmltextgenerator.h" -#include "plaintexteditmodifier.h" -#include "qmldesignerbase/qmldesignerbaseplugin.h" -#include "qmldesignerexternaldependencies.h" -#include "rewriterview.h" - -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include - -namespace { - -inline constexpr char CHILDLISTMODEL_TYPENAME[] = "ChildListModel"; - -QmlDesigner::PropertyNameList createNameList(const QmlDesigner::ModelNode &node) -{ - using QmlDesigner::AbstractProperty; - using QmlDesigner::PropertyName; - using QmlDesigner::PropertyNameList; - static PropertyNameList defaultsNodeProps = { - "id", - QmlDesigner::CollectionEditorConstants::SOURCEFILE_PROPERTY, - QmlDesigner::CollectionEditorConstants::JSONCHILDMODELNAME_PROPERTY, - "backend"}; - PropertyNameList dynamicPropertyNames = Utils::transform( - node.dynamicProperties(), - [](const AbstractProperty &property) -> PropertyName { return property.name(); }); - - Utils::sort(dynamicPropertyNames); - - return defaultsNodeProps + dynamicPropertyNames; -} - -bool isValidCollectionPropertyName(const QString &collectionId) -{ - static const QmlDesigner::PropertyNameList reservedKeywords = { - QmlDesigner::CollectionEditorConstants::SOURCEFILE_PROPERTY, - QmlDesigner::CollectionEditorConstants::JSONBACKEND_TYPENAME, - "backend", - "models", - }; - - return QmlDesigner::ModelNode::isValidId(collectionId) - && !reservedKeywords.contains(collectionId.toLatin1()); -} - -QMap getModelIdMap(const QmlDesigner::ModelNode &rootNode) -{ - using namespace QmlDesigner; - QMap modelNameForId; - - const QList propertyNames = rootNode.dynamicProperties(); - - for (const AbstractProperty &property : std::as_const(propertyNames)) { - if (!property.isNodeProperty()) - continue; - - NodeProperty nodeProperty = property.toNodeProperty(); - if (!nodeProperty.hasDynamicTypeName(CHILDLISTMODEL_TYPENAME)) - continue; - - ModelNode childNode = nodeProperty.modelNode(); - if (childNode.hasProperty(CollectionEditorConstants::JSONCHILDMODELNAME_PROPERTY)) { - QString modelName = childNode - .property(CollectionEditorConstants::JSONCHILDMODELNAME_PROPERTY) - .toVariantProperty() - .value() - .toString(); - - if (!modelName.isEmpty()) - modelNameForId.insert(modelName, property.name()); - } - } - return modelNameForId; -} - -void setQmlContextToModel(QmlDesigner::Model *model, const QString &qmlContext) -{ - using namespace QmlDesigner; - Q_ASSERT(model); - - QScopedPointer textEdit(new QPlainTextEdit); - QScopedPointer modifier( - new NotIndentingTextEditModifier(textEdit.data())); - textEdit->hide(); - textEdit->setPlainText(qmlContext); - QmlDesigner::ExternalDependencies externalDependencies{QmlDesignerBasePlugin::settings()}; - QScopedPointer rewriter( - new RewriterView(externalDependencies, QmlDesigner::RewriterView::Validate)); - - rewriter->setParent(model); - rewriter->setTextModifier(modifier.get()); - rewriter->setCheckSemanticErrors(false); - - model->attachView(rewriter.get()); - model->detachView(rewriter.get()); -} - -} // namespace - -namespace QmlDesigner { - -DataStoreModelNode::DataStoreModelNode() -{ - reloadModel(); -} - -void DataStoreModelNode::reloadModel() -{ - using Utils::FilePath; - if (!ProjectExplorer::ProjectManager::startupProject()) { - reset(); - return; - } - bool forceUpdate = false; - - const FilePath dataStoreQmlPath = CollectionEditorUtils::dataStoreQmlFilePath(); - const FilePath dataStoreJsonPath = CollectionEditorUtils::dataStoreJsonFilePath(); - QUrl dataStoreQmlUrl = dataStoreQmlPath.toUrl(); - - 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( - CollectionEditorConstants::COLLECTIONMODEL_IMPORT); - try { - if (!m_model->hasImport(import, true, true)) - m_model->changeImports({import}, {}); - } catch (const Exception &) { - QTC_ASSERT(false, return); - } -#endif - } - } else { - reset(); - } - - if (!m_model.get()) - return; - - if (forceUpdate) { - m_model->setFileUrl(dataStoreQmlUrl); - m_dataRelativePath = dataStoreJsonPath.relativePathFrom(dataStoreQmlPath).toFSPathString(); - preloadFile(); - update(); - } -} - -QStringList DataStoreModelNode::collectionNames() const -{ - return m_collectionPropertyNames.keys(); -} - -Model *DataStoreModelNode::model() const -{ - return m_model.get(); -} - -ModelNode DataStoreModelNode::modelNode() const -{ - if (!m_model.get()) - return {}; - return m_model->rootModelNode(); -} - -QString DataStoreModelNode::getModelQmlText() -{ - ModelNode node = modelNode(); - QTC_ASSERT(node, return {}); - - Internal::QmlTextGenerator textGen(createNameList(node), - QmlJSTools::QmlJSToolsSettings::globalCodeStyle()->tabSettings()); - - QString genText = textGen(node); - return genText; -} - -void DataStoreModelNode::reset() -{ - if (m_model) - m_model.reset(); - - m_dataRelativePath.clear(); - setCollectionNames({}); -} - -void DataStoreModelNode::preloadFile() -{ - using Utils::FilePath; - using Utils::FileReader; - - if (!m_model) - return; - - const FilePath dataStoreQmlPath = dataStoreQmlFilePath(); - FileReader dataStoreQmlFile; - QString sourceQmlContext; - - if (dataStoreQmlFile.fetch(dataStoreQmlPath)) - sourceQmlContext = QString::fromLatin1(dataStoreQmlFile.data()); - - setQmlContextToModel(m_model.get(), sourceQmlContext); - m_collectionPropertyNames = getModelIdMap(m_model->rootModelNode()); -} - -void DataStoreModelNode::updateDataStoreProperties() -{ - QTC_ASSERT(model(), return); - - ModelNode rootNode = modelNode(); - QTC_ASSERT(rootNode.isValid(), return); - - QSet collectionNamesToBeAdded; - const QStringList allCollectionNames = m_collectionPropertyNames.keys(); - for (const QString &collectionName : allCollectionNames) - collectionNamesToBeAdded << collectionName; - - const QList formerPropertyNames = rootNode.dynamicProperties(); - - // Remove invalid collection names from the properties - for (const AbstractProperty &property : formerPropertyNames) { - if (!property.isNodeProperty()) - continue; - - NodeProperty nodeProprty = property.toNodeProperty(); - if (!nodeProprty.hasDynamicTypeName(CHILDLISTMODEL_TYPENAME)) - continue; - - ModelNode childNode = nodeProprty.modelNode(); - if (childNode.hasProperty(CollectionEditorConstants::JSONCHILDMODELNAME_PROPERTY)) { - QString modelName = childNode - .property(CollectionEditorConstants::JSONCHILDMODELNAME_PROPERTY) - .toVariantProperty() - .value() - .toString(); - if (collectionNamesToBeAdded.contains(modelName)) { - m_collectionPropertyNames.insert(modelName, property.name()); - collectionNamesToBeAdded.remove(modelName); - } else { - rootNode.removeProperty(property.name()); - } - } else { - rootNode.removeProperty(property.name()); - } - } - - rootNode.setIdWithoutRefactoring("models"); - - QStringList collectionNamesLeft = collectionNamesToBeAdded.values(); - Utils::sort(collectionNamesLeft); - for (const QString &collectionName : std::as_const(collectionNamesLeft)) - addCollectionNameToTheModel(collectionName, getUniquePropertyName(collectionName)); - - // Backend Property - ModelNode backendNode = model()->createModelNode(CollectionEditorConstants::JSONBACKEND_TYPENAME); - NodeProperty backendProperty = rootNode.nodeProperty("backend"); - backendProperty.setDynamicTypeNameAndsetModelNode(CollectionEditorConstants::JSONBACKEND_TYPENAME, - backendNode); - // Source Property - VariantProperty sourceProp = rootNode.variantProperty( - CollectionEditorConstants::SOURCEFILE_PROPERTY); - sourceProp.setValue(m_dataRelativePath); -} - -void DataStoreModelNode::updateSingletonFile() -{ - using Utils::FilePath; - using Utils::FileSaver; - QTC_ASSERT(m_model.get(), return); - - const QString pragmaSingleTone = "pragma Singleton\n"; - QString imports; - - for (const Import &import : m_model->imports()) - 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(); -} - -void DataStoreModelNode::update() -{ - if (!m_model.get()) - return; - - updateDataStoreProperties(); - updateSingletonFile(); -} - -void DataStoreModelNode::addCollectionNameToTheModel(const QString &collectionName, - const PropertyName &dataStorePropertyName) -{ - ModelNode rootNode = modelNode(); - QTC_ASSERT(rootNode.isValid(), return); - - if (dataStorePropertyName.isEmpty()) { - qWarning() << __FUNCTION__ << __LINE__ - << QString("The property name cannot be generated from \"%1\"").arg(collectionName); - return; - } - - ModelNode collectionNode = model()->createModelNode(CHILDLISTMODEL_TYPENAME); - VariantProperty modelNameProperty = collectionNode.variantProperty( - CollectionEditorConstants::JSONCHILDMODELNAME_PROPERTY); - modelNameProperty.setValue(collectionName); - - NodeProperty nodeProp = rootNode.nodeProperty(dataStorePropertyName); - nodeProp.setDynamicTypeNameAndsetModelNode(CHILDLISTMODEL_TYPENAME, collectionNode); - - m_collectionPropertyNames.insert(collectionName, dataStorePropertyName); -} - -Utils::FilePath DataStoreModelNode::dataStoreQmlFilePath() const -{ - QUrl modelUrl = m_model->fileUrl(); - return Utils::FilePath::fromUserInput(modelUrl.isLocalFile() ? modelUrl.toLocalFile() - : modelUrl.toString()); -} - -PropertyName DataStoreModelNode::getUniquePropertyName(const QString &collectionName) -{ - ModelNode dataStoreNode = modelNode(); - QTC_ASSERT(!collectionName.isEmpty() && dataStoreNode.isValid(), return {}); - - QString newProperty; - - // convert to camel case - QStringList nameWords = collectionName.split(' '); - nameWords[0] = nameWords[0].at(0).toLower() + nameWords[0].mid(1); - for (int i = 1; i < nameWords.size(); ++i) - nameWords[i] = nameWords[i].at(0).toUpper() + nameWords[i].mid(1); - newProperty = nameWords.join(""); - - // if id starts with a number prepend an underscore - if (newProperty.at(0).isDigit()) - newProperty.prepend('_'); - - // If the new id is not valid (e.g. qml keyword match), prepend an underscore - if (!isValidCollectionPropertyName(newProperty)) - newProperty.prepend('_'); - - static const QRegularExpression rgx("\\d+$"); // matches a number at the end of a string - while (dataStoreNode.hasProperty(newProperty.toLatin1())) { // id exists - QRegularExpressionMatch match = rgx.match(newProperty); - if (match.hasMatch()) { // ends with a number, increment it - QString numStr = match.captured(); - int num = numStr.toInt() + 1; - newProperty = newProperty.mid(0, match.capturedStart()) + QString::number(num); - } else { - newProperty.append('1'); - } - } - - return newProperty.toLatin1(); -} - -void DataStoreModelNode::setCollectionNames(const QStringList &newCollectionNames) -{ - m_collectionPropertyNames.clear(); - for (const QString &collectionName : newCollectionNames) - m_collectionPropertyNames.insert(collectionName, {}); - 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(); - QTC_ASSERT(dataStoreNode.isValid(), return); - - if (m_collectionPropertyNames.contains(oldName)) { - const PropertyName oldPropertyName = m_collectionPropertyNames.value(oldName); - if (!oldPropertyName.isEmpty() && dataStoreNode.hasProperty(oldPropertyName)) { - NodeProperty collectionNode = dataStoreNode.property(oldPropertyName).toNodeProperty(); - if (collectionNode.isValid()) { - VariantProperty modelNameProperty = collectionNode.modelNode().variantProperty( - CollectionEditorConstants::JSONCHILDMODELNAME_PROPERTY); - modelNameProperty.setValue(newName); - m_collectionPropertyNames.remove(oldName); - m_collectionPropertyNames.insert(newName, collectionNode.name()); - update(); - return; - } - qWarning() << __FUNCTION__ << __LINE__ - << "There is no valid node for the old collection name"; - return; - } - qWarning() << __FUNCTION__ << __LINE__ << QString("Invalid old property name") - << oldPropertyName; - return; - } - qWarning() << __FUNCTION__ << __LINE__ - << QString("There is no old collection name registered with this name \"%1\"").arg(oldName); -} - -void DataStoreModelNode::removeCollections(const QStringList &collectionNames) -{ - 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, - const ModelNode &targetNode, - const QString &collectionName, - CollectionColumnFinder collectionHasColumn, - FirstColumnProvider firstColumnProvider) -{ - QTC_ASSERT(targetNode.isValid(), return); - - if (!CollectionEditorUtils::canAcceptCollectionAsModel(targetNode)) - return; - - if (!m_collectionPropertyNames.contains(collectionName)) { - qWarning() << __FUNCTION__ << __LINE__ << "Collection doesn't exist in the DataStore" - << collectionName; - return; - } - - PropertyName propertyName = m_collectionPropertyNames.value(collectionName); - - const ModelNode dataStore = modelNode(); - VariantProperty sourceProperty = dataStore.variantProperty(propertyName); - if (!sourceProperty.exists()) { - qWarning() << __FUNCTION__ << __LINE__ - << "The source property doesn't exist in the DataStore."; - return; - } - - 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(); - - if (currentTextRoleValue.isValid() && !currentTextRoleValue.isNull()) { - if (currentTextRoleValue.typeId() == QMetaType::QString) { - const QString currentTextRole = currentTextRoleValue.toString(); - if (collectionHasColumn(collectionName, currentTextRole)) - return; - } else { - return; - } - } - - QString textRoleValue = firstColumnProvider(collectionName); - textRoleProperty.setValue(textRoleValue); - } - }); -} - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.h b/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.h deleted file mode 100644 index 6cd969edbe6..00000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.h +++ /dev/null @@ -1,63 +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 - -#pragma once - -#include - -#include - -namespace Utils { -class FilePath; -} - -namespace QmlDesigner { - -class Model; - -class DataStoreModelNode -{ -public: - using CollectionColumnFinder = std::function; - using FirstColumnProvider = std::function; - - DataStoreModelNode(); - - void reloadModel(); - QStringList collectionNames() const; - - Model *model() const; - ModelNode modelNode() const; - - void setCollectionNames(const QStringList &newCollectionNames); - void addCollection(const QString &collectionName); - void renameCollection(const QString &oldName, const QString &newName); - void removeCollections(const QStringList &collectionNames); - - void assignCollectionToNode(AbstractView *view, - const ModelNode &targetNode, - const QString &collectionName, - CollectionColumnFinder collectionHasColumn, - FirstColumnProvider firstColumnProvider); - -private: - QString getModelQmlText(); - - void reset(); - void preloadFile(); - void updateDataStoreProperties(); - void updateSingletonFile(); - void update(); - void addCollectionNameToTheModel(const QString &collectionName, - const PropertyName &dataStorePropertyName); - Utils::FilePath dataStoreQmlFilePath() const; - - PropertyName getUniquePropertyName(const QString &collectionName); - - ModelPointer m_model; - QMap m_collectionPropertyNames; - QString m_dataRelativePath; -}; - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/componentcore/abstractaction.cpp b/src/plugins/qmldesigner/components/componentcore/abstractaction.cpp index 559e8ea69c6..3379d998349 100644 --- a/src/plugins/qmldesigner/components/componentcore/abstractaction.cpp +++ b/src/plugins/qmldesigner/components/componentcore/abstractaction.cpp @@ -8,7 +8,7 @@ namespace QmlDesigner { AbstractAction::AbstractAction(const QString &description) - : m_pureAction(new DefaultAction(description)) + : m_pureAction(std::make_unique(description)) { const Utils::Icon defaultIcon({ {":/utils/images/select.png", Utils::Theme::QmlDesigner_FormEditorForegroundColor}}, Utils::Icon::MenuTintedStyle); @@ -56,7 +56,7 @@ void AbstractAction::setCheckable(bool checkable) PureActionInterface *AbstractAction::pureAction() const { - return m_pureAction.data(); + return m_pureAction.get(); } SelectionContext AbstractAction::selectionContext() const diff --git a/src/plugins/qmldesigner/components/componentcore/abstractaction.h b/src/plugins/qmldesigner/components/componentcore/abstractaction.h index ca4cc582ce9..53b540cc7a3 100644 --- a/src/plugins/qmldesigner/components/componentcore/abstractaction.h +++ b/src/plugins/qmldesigner/components/componentcore/abstractaction.h @@ -6,7 +6,8 @@ #include "actioninterface.h" #include -#include + +#include namespace QmlDesigner { @@ -58,7 +59,7 @@ protected: SelectionContext selectionContext() const; private: - QScopedPointer m_pureAction; + std::unique_ptr m_pureAction; SelectionContext m_selectionContext; }; diff --git a/src/plugins/qmldesigner/components/componentcore/abstractactiongroup.cpp b/src/plugins/qmldesigner/components/componentcore/abstractactiongroup.cpp index 288b8e409de..5b340343e7d 100644 --- a/src/plugins/qmldesigner/components/componentcore/abstractactiongroup.cpp +++ b/src/plugins/qmldesigner/components/componentcore/abstractactiongroup.cpp @@ -8,14 +8,14 @@ namespace QmlDesigner { -AbstractActionGroup::AbstractActionGroup(const QString &displayName) : - m_displayName(displayName), - m_menu(new QmlEditorMenu) +AbstractActionGroup::AbstractActionGroup(const QString &displayName) + : m_displayName(displayName) + , m_menu(Utils::makeUniqueObjectPtr()) { m_menu->setTitle(displayName); m_action = m_menu->menuAction(); - QmlEditorMenu *qmlEditorMenu = qobject_cast(m_menu.data()); + QmlEditorMenu *qmlEditorMenu = qobject_cast(m_menu.get()); if (qmlEditorMenu) qmlEditorMenu->setIconsVisible(false); } @@ -32,7 +32,7 @@ QAction *AbstractActionGroup::action() const QMenu *AbstractActionGroup::menu() const { - return m_menu.data(); + return m_menu.get(); } SelectionContext AbstractActionGroup::selectionContext() const diff --git a/src/plugins/qmldesigner/components/componentcore/abstractactiongroup.h b/src/plugins/qmldesigner/components/componentcore/abstractactiongroup.h index dd89849ecfa..f239eeab3de 100644 --- a/src/plugins/qmldesigner/components/componentcore/abstractactiongroup.h +++ b/src/plugins/qmldesigner/components/componentcore/abstractactiongroup.h @@ -5,9 +5,10 @@ #include "actioninterface.h" +#include + #include #include -#include namespace QmlDesigner { @@ -29,7 +30,7 @@ public: private: const QString m_displayName; SelectionContext m_selectionContext; - QScopedPointer m_menu; + Utils::UniqueObjectPtr m_menu; QAction *m_action; }; diff --git a/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h b/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h index d992a6a5bf0..da7c5bf72eb 100644 --- a/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h +++ b/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h @@ -69,7 +69,6 @@ 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"; @@ -128,7 +127,6 @@ 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"); const char editIn3dViewDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Edit in 3D View"); @@ -214,7 +212,6 @@ enum PrioritiesEnum : int { 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 8d2b2c43c2a..bbe64935f61 100644 --- a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp +++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp @@ -113,7 +113,6 @@ 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); @@ -121,7 +120,6 @@ void DesignerActionManager::polishActions() const qmlDesignerUIContext.add(qmlDesignerNavigatorContext); qmlDesignerUIContext.add(qmlDesignerMaterialBrowserContext); qmlDesignerUIContext.add(qmlDesignerAssetsLibraryContext); - qmlDesignerUIContext.add(qmlDesignerCollectionEditorContext); for (auto *action : actions) { if (!action->menuId().isEmpty()) { @@ -1988,8 +1986,8 @@ void DesignerActionManager::createDefaultDesignerActions() QKeySequence(), 44, &editMaterial, - &modelHasMaterial, - &isModel)); + &hasEditableMaterial, + &isModelOrMaterial)); addDesignerAction(new ModelNodeContextMenuAction( mergeTemplateCommandId, @@ -2011,16 +2009,6 @@ 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, {}, @@ -2193,7 +2181,8 @@ void DesignerActionManager::addCustomTransitionEffectAction() void DesignerActionManager::setupIcons() { - m_designerIcons.reset(new DesignerIcons("qtds_propertyIconFont.ttf", designerIconResourcesPath())); + m_designerIcons = std::make_unique("qtds_propertyIconFont.ttf", + designerIconResourcesPath()); } QString DesignerActionManager::designerIconResourcesPath() const diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.h b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.h index 16d6219cd69..89505fcbe85 100644 --- a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.h +++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.h @@ -138,7 +138,7 @@ private: QList m_addResourceHandler; QList m_modelNodePreviewImageHandlers; ExternalDependenciesInterface &m_externalDependencies; - QScopedPointer m_designerIcons; + std::unique_ptr m_designerIcons; QList m_callBacks; }; diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h b/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h index aec14e9d04b..6734bac568b 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h +++ b/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h @@ -64,33 +64,24 @@ inline bool addMouseAreaFillCheck(const SelectionContext &selectionContext) return false; } -inline bool isModel(const SelectionContext &selectionState) +inline bool isModelOrMaterial(const SelectionContext &selectionState) { ModelNode node = selectionState.currentSingleSelectedNode(); - return node.metaInfo().isQtQuick3DModel(); + return node.metaInfo().isQtQuick3DModel() || node.metaInfo().isQtQuick3DMaterial(); } -inline bool modelHasMaterial(const SelectionContext &selectionState) +inline bool hasEditableMaterial(const SelectionContext &selectionState) { ModelNode node = selectionState.currentSingleSelectedNode(); + if (node.metaInfo().isQtQuick3DMaterial()) + return true; + BindingProperty prop = node.bindingProperty("materials"); 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 f46b2daf78d..bf8e78a2c7c 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp @@ -817,21 +817,25 @@ void editMaterial(const SelectionContext &selectionContext) QTC_ASSERT(modelNode.isValid(), return); - BindingProperty prop = modelNode.bindingProperty("materials"); - if (!prop.exists()) - return; - AbstractView *view = selectionContext.view(); ModelNode material; - if (view->hasId(prop.expression())) { - material = view->modelNodeForId(prop.expression()); + if (modelNode.metaInfo().isQtQuick3DMaterial()) { + material = modelNode; } else { - QList materials = prop.resolveToModelNodeList(); + BindingProperty prop = modelNode.bindingProperty("materials"); + if (!prop.exists()) + return; - if (materials.size() > 0) - material = materials.first(); + if (view->hasId(prop.expression())) { + material = view->modelNodeForId(prop.expression()); + } else { + QList materials = prop.resolveToModelNodeList(); + + if (materials.size() > 0) + material = materials.first(); + } } if (material.isValid()) { @@ -842,30 +846,6 @@ 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 a67cef49424..26562f429a0 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h @@ -92,7 +92,6 @@ 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/componentcore/propertycomponentgenerator.cpp b/src/plugins/qmldesigner/components/componentcore/propertycomponentgenerator.cpp index e7e56e72b98..58326dc77a3 100644 --- a/src/plugins/qmldesigner/components/componentcore/propertycomponentgenerator.cpp +++ b/src/plugins/qmldesigner/components/componentcore/propertycomponentgenerator.cpp @@ -179,7 +179,7 @@ std::optional createEntry(QmlJS::SimpleReader if (moduleName.isEmpty()) return {}; - auto module = model->module(moduleName); + auto module = model->module(moduleName, Storage::ModuleKind::QmlLibrary); auto typeName = getProperty(node, "typeNames"); diff --git a/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp b/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp index b011d9fbbf3..a56735862f3 100644 --- a/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp +++ b/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp @@ -7,7 +7,6 @@ #include #include #include -#include #include #include #include @@ -42,14 +41,6 @@ namespace QmlDesigner { -static bool enableModelEditor() -{ - Utils::QtcSettings *settings = Core::ICore::settings(); - const Utils::Key enableModelManagerKey = "QML/Designer/UseExperimentalFeatures44"; - - return settings->value(enableModelManagerKey, false).toBool(); -} - static Q_LOGGING_CATEGORY(viewBenchmark, "qtc.viewmanager.attach", QtWarningMsg) class ViewManagerData @@ -64,8 +55,7 @@ public: : connectionManager, externalDependencies, true) - , collectionView{externalDependencies} - , contentLibraryView{externalDependencies} + , contentLibraryView{imageCache, externalDependencies} , componentView{externalDependencies} #ifndef QTC_USE_QML_DESIGNER_LITE , edit3DView{externalDependencies} @@ -90,7 +80,6 @@ public: Internal::DebugView debugView; DesignerActionManagerView designerActionManagerView; NodeInstanceView nodeInstanceView; - CollectionView collectionView; ContentLibraryView contentLibraryView; ComponentView componentView; #ifndef QTC_USE_QML_DESIGNER_LITE @@ -235,9 +224,6 @@ QList ViewManager::standardViews() const &d->designerActionManagerView}; #endif - if (enableModelEditor()) - list.append(&d->collectionView); - if (QmlDesignerPlugin::instance() ->settings() .value(DesignerSettingsKey::ENABLE_DEBUGVIEW) @@ -418,8 +404,6 @@ QList ViewManager::widgetInfos() const widgetInfoList.append(d->textureEditorView.widgetInfo()); #endif widgetInfoList.append(d->statesEditorView.widgetInfo()); - if (enableModelEditor()) - widgetInfoList.append(d->collectionView.widgetInfo()); if (checkEnterpriseLicense()) widgetInfoList.append(d->contentLibraryView.widgetInfo()); diff --git a/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp index f871bae84b4..2cee7b0f977 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp @@ -22,6 +22,7 @@ namespace QmlDesigner { BindingModel::BindingModel(ConnectionView *view) : m_connectionView(view) + , m_delegate(*this) { setHorizontalHeaderLabels(BindingModelItem::headerLabels()); } @@ -246,11 +247,8 @@ void BindingModel::addModelNode(const ModelNode &node) appendRow(new BindingModelItem(property)); } -BindingModelBackendDelegate::BindingModelBackendDelegate() - : m_targetNode() - , m_property() - , m_sourceNode() - , m_sourceNodeProperty() +BindingModelBackendDelegate::BindingModelBackendDelegate(BindingModel &model) + : m_model{model} { connect(&m_sourceNode, &StudioQmlComboBoxBackend::activated, this, [this] { sourceNodeChanged(); @@ -322,17 +320,14 @@ StudioQmlComboBoxBackend *BindingModelBackendDelegate::sourceProperty() void BindingModelBackendDelegate::sourceNodeChanged() { - BindingModel *model = qobject_cast(parent()); - QTC_ASSERT(model, return); - - ConnectionView *view = model->connectionView(); + ConnectionView *view = m_model.connectionView(); QTC_ASSERT(view, return); QTC_ASSERT(view->isAttached(), return ); const QString sourceNode = m_sourceNode.currentText(); const QString sourceProperty = m_sourceNodeProperty.currentText(); - BindingProperty targetProperty = model->currentProperty(); + BindingProperty targetProperty = m_model.currentProperty(); QStringList properties = availableSourceProperties(sourceNode, targetProperty, view); if (!properties.contains(sourceProperty)) { @@ -351,9 +346,6 @@ void BindingModelBackendDelegate::sourcePropertyNameChanged() const return; auto commit = [this, sourceProperty]() { - BindingModel *model = qobject_cast(parent()); - QTC_ASSERT(model, return); - const QString sourceNode = m_sourceNode.currentText(); QString expression; if (sourceProperty.isEmpty()) @@ -361,8 +353,8 @@ void BindingModelBackendDelegate::sourcePropertyNameChanged() const else expression = sourceNode + QLatin1String(".") + sourceProperty; - int row = model->currentIndex(); - model->commitExpression(row, expression); + int row = m_model.currentIndex(); + m_model.commitExpression(row, expression); }; callLater(commit); @@ -371,11 +363,9 @@ void BindingModelBackendDelegate::sourcePropertyNameChanged() const void BindingModelBackendDelegate::targetPropertyNameChanged() const { auto commit = [this] { - BindingModel *model = qobject_cast(parent()); - QTC_ASSERT(model, return); const PropertyName propertyName = m_property.currentText().toUtf8(); - int row = model->currentIndex(); - model->commitPropertyName(row, propertyName); + int row = m_model.currentIndex(); + m_model.commitPropertyName(row, propertyName); }; callLater(commit); diff --git a/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.h b/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.h index b57cc5c9587..69b137e78a5 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.h +++ b/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.h @@ -30,7 +30,7 @@ signals: void targetNodeChanged(); public: - BindingModelBackendDelegate(); + BindingModelBackendDelegate(class BindingModel &model); void update(const BindingProperty &property, AbstractView *view); @@ -44,6 +44,7 @@ private: StudioQmlComboBoxBackend *sourceNode(); StudioQmlComboBoxBackend *sourceProperty(); + BindingModel &m_model; QString m_targetNode; StudioQmlComboBoxBackend m_property; StudioQmlComboBoxBackend m_sourceNode; diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectioneditorutils.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorutils.cpp index 57ca619a70a..3cbfb8c0383 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectioneditorutils.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorutils.cpp @@ -212,7 +212,8 @@ bool isDynamicVariantPropertyType(const TypeName &type) { // "variant" is considered value type as it is initialized as one. // This may need to change if we provide any kind of proper editor for it. - static const QSet valueTypes{"int", "real", "color", "string", "bool", "url", "var", "variant"}; + static const QSet valueTypes{ + "int", "real", "double", "color", "string", "bool", "url", "var", "variant"}; return valueTypes.contains(type); } diff --git a/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp index 9fdd3daec31..fa29c6c8a1b 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp @@ -25,7 +25,7 @@ namespace QmlDesigner { DynamicPropertiesModel::DynamicPropertiesModel(bool exSelection, AbstractView *view) : m_view(view) - , m_delegate(std::make_unique()) + , m_delegate(std::make_unique(*this)) , m_explicitSelection(exSelection) { setHorizontalHeaderLabels(DynamicPropertiesItem::headerLabels()); @@ -382,8 +382,8 @@ void DynamicPropertiesModel::setSelectedNode(const ModelNode &node) reset(); } -DynamicPropertiesModelBackendDelegate::DynamicPropertiesModelBackendDelegate() - : m_internalNodeId(std::nullopt) +DynamicPropertiesModelBackendDelegate::DynamicPropertiesModelBackendDelegate(DynamicPropertiesModel &model) + : m_model(model) { m_type.setModel({"int", "bool", "var", "real", "string", "url", "color"}); connect(&m_type, &StudioQmlComboBoxBackend::activated, this, [this] { handleTypeChanged(); }); @@ -411,32 +411,26 @@ void DynamicPropertiesModelBackendDelegate::update(const AbstractProperty &prope void DynamicPropertiesModelBackendDelegate::handleTypeChanged() { - DynamicPropertiesModel *model = qobject_cast(parent()); - QTC_ASSERT(model, return); - const PropertyName name = m_name.text().toUtf8(); - int current = model->currentIndex(); + int current = m_model.currentIndex(); const TypeName type = m_type.currentText().toUtf8(); - model->commitPropertyType(current, type); + m_model.commitPropertyType(current, type); // The order might have changed! - model->setCurrent(m_internalNodeId.value_or(-1), name); + m_model.setCurrent(m_internalNodeId.value_or(-1), name); } void DynamicPropertiesModelBackendDelegate::handleNameChanged() { - DynamicPropertiesModel *model = qobject_cast(parent()); - QTC_ASSERT(model, return); - const PropertyName name = m_name.text().toUtf8(); QTC_ASSERT(!name.isEmpty(), return); - int current = model->currentIndex(); - model->commitPropertyName(current, name); + int current = m_model.currentIndex(); + m_model.commitPropertyName(current, name); // The order might have changed! - model->setCurrent(m_internalNodeId.value_or(-1), name); + m_model.setCurrent(m_internalNodeId.value_or(-1), name); } // TODO: Maybe replace with utils typeConvertVariant? @@ -456,12 +450,9 @@ QVariant valueFromText(const QString &value, const QString &type) void DynamicPropertiesModelBackendDelegate::handleValueChanged() { - DynamicPropertiesModel *model = qobject_cast(parent()); - QTC_ASSERT(model, return); - - int current = model->currentIndex(); + int current = m_model.currentIndex(); QVariant value = valueFromText(m_value.text(), m_type.currentText()); - model->commitPropertyValue(current, value); + m_model.commitPropertyValue(current, value); } QString DynamicPropertiesModelBackendDelegate::targetNode() const diff --git a/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.h b/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.h index c2beed87304..071c72bef81 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.h +++ b/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.h @@ -97,7 +97,7 @@ class DynamicPropertiesModelBackendDelegate : public QObject Q_PROPERTY(StudioQmlTextBackend *value READ value CONSTANT) public: - DynamicPropertiesModelBackendDelegate(); + DynamicPropertiesModelBackendDelegate(DynamicPropertiesModel &model); void update(const AbstractProperty &property); @@ -116,6 +116,7 @@ private: StudioQmlTextBackend *value(); QString targetNode() const; + DynamicPropertiesModel &m_model; std::optional m_internalNodeId; StudioQmlComboBoxBackend m_type; StudioQmlTextBackend m_name; diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.cpp index 5c8d42a3065..6388c93fa96 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.cpp @@ -3,32 +3,26 @@ #include "contentlibrarybundleimporter.h" -#include "documentmanager.h" -#include "import.h" -#include "model.h" -#include "qmldesignerconstants.h" -#include "qmldesignerplugin.h" -#include "rewritingexception.h" +#include +#include +#include +#include +#include +#include +#include #include -#include #include #include #include using namespace Utils; -namespace QmlDesigner::Internal { +namespace QmlDesigner { -ContentLibraryBundleImporter::ContentLibraryBundleImporter(const QString &bundleDir, - const QString &bundleId, - const QStringList &sharedFiles, - QObject *parent) +ContentLibraryBundleImporter::ContentLibraryBundleImporter(QObject *parent) : QObject(parent) - , m_bundleDir(FilePath::fromString(bundleDir)) - , m_bundleId(bundleId) - , m_sharedFiles(sharedFiles) { m_importTimer.setInterval(200); connect(&m_importTimer, &QTimer::timeout, this, &ContentLibraryBundleImporter::handleImportTimer); @@ -38,47 +32,38 @@ ContentLibraryBundleImporter::ContentLibraryBundleImporter(const QString &bundle // Note that there is also an asynchronous portion to the import, which will only // be done if this method returns success. Once the asynchronous portion of the // import is completed, importFinished signal will be emitted. -QString ContentLibraryBundleImporter::importComponent(const QString &qmlFile, +QString ContentLibraryBundleImporter::importComponent(const QString &bundleDir, + const TypeName &type, + const QString &qmlFile, const QStringList &files) { - FilePath bundleImportPath = resolveBundleImportPath(); + QString module = QString::fromLatin1(type.left(type.lastIndexOf('.'))); + m_bundleId = module.mid(module.lastIndexOf('.') + 1); + + FilePath bundleDirPath = FilePath::fromString(bundleDir); // source dir + FilePath bundleImportPath = resolveBundleImportPath(m_bundleId); // target dir + if (bundleImportPath.isEmpty()) return "Failed to resolve bundle import folder"; - bool bundleImportPathExists = bundleImportPath.exists(); - - if (!bundleImportPathExists && !bundleImportPath.createDir()) + if (!bundleImportPath.exists() && !bundleImportPath.createDir()) return QStringLiteral("Failed to create bundle import folder: '%1'").arg(bundleImportPath.toString()); - for (const QString &file : std::as_const(m_sharedFiles)) { - FilePath target = bundleImportPath.resolvePath(file); - if (!target.exists()) { - FilePath parentDir = target.parentDir(); - if (!parentDir.exists() && !parentDir.createDir()) - return QStringLiteral("Failed to create folder for: '%1'").arg(target.toString()); - FilePath source = m_bundleDir.resolvePath(file); - if (!source.copyFile(target)) - return QStringLiteral("Failed to copy shared file: '%1'").arg(source.toString()); - } - } - - FilePath qmldirPath = bundleImportPath.resolvePath(QStringLiteral("qmldir")); + FilePath qmldirPath = bundleImportPath.pathAppended("qmldir"); QString qmldirContent = QString::fromUtf8(qmldirPath.fileContents().value_or(QByteArray())); if (qmldirContent.isEmpty()) { qmldirContent.append("module "); - qmldirContent.append(moduleName()); + qmldirContent.append(module); qmldirContent.append('\n'); } - FilePath qmlSourceFile = bundleImportPath.resolvePath(FilePath::fromString(qmlFile)); + FilePath qmlSourceFile = bundleImportPath.pathAppended(qmlFile); const bool qmlFileExists = qmlSourceFile.exists(); const QString qmlType = qmlSourceFile.baseName(); - const QString fullTypeName = QStringLiteral("%1.%2.%3") - .arg(QmlDesignerPlugin::instance()->documentManager() - .generatedComponentUtils().componentBundlesTypePrefix(), - m_bundleId, qmlType); - if (m_pendingTypes.contains(fullTypeName) && !m_pendingTypes[fullTypeName]) - return QStringLiteral("Unable to import while unimporting the same type: '%1'").arg(fullTypeName); + + if (m_pendingTypes.contains(type) && !m_pendingTypes.value(type)) + return QStringLiteral("Unable to import while unimporting the same type: '%1'").arg(QLatin1String(type)); + if (!qmldirContent.contains(qmlFile)) { qmldirContent.append(qmlType); qmldirContent.append(" 1.0 "); @@ -91,12 +76,12 @@ QString ContentLibraryBundleImporter::importComponent(const QString &qmlFile, allFiles.append(files); allFiles.append(qmlFile); for (const QString &file : std::as_const(allFiles)) { - FilePath target = bundleImportPath.resolvePath(file); + FilePath target = bundleImportPath.pathAppended(file); FilePath parentDir = target.parentDir(); if (!parentDir.exists() && !parentDir.createDir()) return QStringLiteral("Failed to create folder for: '%1'").arg(target.toString()); - FilePath source = m_bundleDir.resolvePath(file); + FilePath source = bundleDirPath.pathAppended(file); if (target.exists()) { if (source.lastModified() == target.lastModified()) continue; @@ -125,23 +110,23 @@ QString ContentLibraryBundleImporter::importComponent(const QString &qmlFile, if (!model) return "Model not available, cannot add import statement or update code model"; - Import import = Import::createLibraryImport(moduleName(), "1.0"); + Import import = Import::createLibraryImport(module, "1.0"); if (!model->hasImport(import)) { if (model->possibleImports().contains(import)) { - m_importAddPending = false; + m_pendingImport.clear(); try { model->changeImports({import}, {}); } catch (const RewritingException &) { // No point in trying to add import asynchronously either, so just fail out - return QStringLiteral("Failed to add import statement for: '%1'").arg(moduleName()); + return QStringLiteral("Failed to add import statement for: '%1'").arg(module); } } else { // If import is not yet possible, import statement needs to be added asynchronously to // avoid errors, as code model update takes a while. - m_importAddPending = true; + m_pendingImport = module; } } - m_pendingTypes.insert(fullTypeName, true); + m_pendingTypes.insert(type, true); m_importTimerCount = 0; m_importTimer.start(); @@ -153,17 +138,19 @@ void ContentLibraryBundleImporter::handleImportTimer() auto handleFailure = [this] { m_importTimer.stop(); m_fullReset = false; - m_importAddPending = false; + m_pendingImport.clear(); m_importTimerCount = 0; // Emit dummy finished signals for all pending types - const QStringList pendingTypes = m_pendingTypes.keys(); - for (const QString &pendingType : pendingTypes) { + const QList pendingTypes = m_pendingTypes.keys(); + for (const TypeName &pendingType : pendingTypes) { m_pendingTypes.remove(pendingType); - if (m_pendingTypes[pendingType]) - emit importFinished({}); + if (m_pendingTypes.value(pendingType)) + emit importFinished({}, m_bundleId); else - emit unimportFinished({}); + emit unimportFinished({}, m_bundleId); + + m_bundleId.clear(); } }; @@ -185,12 +172,12 @@ void ContentLibraryBundleImporter::handleImportTimer() QmlDesignerPlugin::instance()->documentManager().resetPossibleImports(); - if (m_importAddPending) { + if (!m_pendingImport.isEmpty()) { try { - Import import = Import::createLibraryImport(moduleName(), "1.0"); + Import import = Import::createLibraryImport(m_pendingImport, "1.0"); if (model->possibleImports().contains(import)) { model->changeImports({import}, {}); - m_importAddPending = false; + m_pendingImport.clear(); } } catch (const RewritingException &) { // Import adding is unlikely to succeed later, either, so just bail out @@ -200,21 +187,23 @@ void ContentLibraryBundleImporter::handleImportTimer() } // Detect when the code model has the new material(s) fully available - const QStringList pendingTypes = m_pendingTypes.keys(); - for (const QString &pendingType : pendingTypes) { - NodeMetaInfo metaInfo = model->metaInfo(pendingType.toUtf8()); - const bool isImport = m_pendingTypes[pendingType]; + const QList pendingTypes = m_pendingTypes.keys(); + for (const TypeName &pendingType : pendingTypes) { + NodeMetaInfo metaInfo = model->metaInfo(pendingType); + const bool isImport = m_pendingTypes.value(pendingType); const bool typeComplete = metaInfo.isValid() && !metaInfo.prototypes().empty(); if (isImport == typeComplete) { m_pendingTypes.remove(pendingType); if (isImport) #ifdef QDS_USE_PROJECTSTORAGE - emit importFinished(pendingType.toUtf8()); + emit importFinished(pendingType, m_bundleId); #else - emit importFinished(metaInfo); + emit importFinished(metaInfo, m_bundleId); #endif else - emit unimportFinished(metaInfo); + emit unimportFinished(metaInfo, m_bundleId); + + m_bundleId.clear(); } } @@ -224,10 +213,10 @@ void ContentLibraryBundleImporter::handleImportTimer() } } -QVariantHash ContentLibraryBundleImporter::loadAssetRefMap(const Utils::FilePath &bundlePath) +QVariantHash ContentLibraryBundleImporter::loadAssetRefMap(const FilePath &bundlePath) { FilePath assetRefPath = bundlePath.resolvePath(QLatin1String(Constants::COMPONENT_BUNDLES_ASSET_REF_FILE)); - const Utils::expected_str content = assetRefPath.fileContents(); + const expected_str content = assetRefPath.fileContents(); if (content) { QJsonParseError error; QJsonDocument bundleDataJsonDoc = QJsonDocument::fromJson(*content, &error); @@ -241,7 +230,7 @@ QVariantHash ContentLibraryBundleImporter::loadAssetRefMap(const Utils::FilePath return {}; } -void ContentLibraryBundleImporter::writeAssetRefMap(const Utils::FilePath &bundlePath, +void ContentLibraryBundleImporter::writeAssetRefMap(const FilePath &bundlePath, const QVariantHash &assetRefMap) { FilePath assetRefPath = bundlePath.resolvePath(QLatin1String(Constants::COMPONENT_BUNDLES_ASSET_REF_FILE)); @@ -252,16 +241,14 @@ void ContentLibraryBundleImporter::writeAssetRefMap(const Utils::FilePath &bundl } } -QString ContentLibraryBundleImporter::moduleName() +QString ContentLibraryBundleImporter::unimportComponent(const TypeName &type, const QString &qmlFile) { - return QStringLiteral("%1.%2").arg(QmlDesignerPlugin::instance()->documentManager() - .generatedComponentUtils().componentBundlesTypePrefix(), - m_bundleId); -} + QString module = QString::fromLatin1(type.left(type.lastIndexOf('.'))); + m_bundleId = module.mid(module.lastIndexOf('.') + 1); -QString ContentLibraryBundleImporter::unimportComponent(const QString &qmlFile) -{ - FilePath bundleImportPath = resolveBundleImportPath(); + emit aboutToUnimport(type, m_bundleId); + + FilePath bundleImportPath = resolveBundleImportPath(m_bundleId); if (bundleImportPath.isEmpty()) return QStringLiteral("Failed to resolve bundle import folder for: '%1'").arg(qmlFile); @@ -280,12 +267,10 @@ QString ContentLibraryBundleImporter::unimportComponent(const QString &qmlFile) QByteArray newContent; QString qmlType = qmlFilePath.baseName(); - const QString fullTypeName = QStringLiteral("%1.%2.%3") - .arg(QmlDesignerPlugin::instance()->documentManager() - .generatedComponentUtils().componentBundlesTypePrefix(), - m_bundleId, qmlType); - if (m_pendingTypes.contains(fullTypeName) && m_pendingTypes[fullTypeName]) - return QStringLiteral("Unable to unimport while importing the same type: '%1'").arg(fullTypeName); + if (m_pendingTypes.contains(type) && m_pendingTypes.value(type)) { + return QStringLiteral("Unable to unimport while importing the same type: '%1'") + .arg(QString::fromLatin1(type)); + } if (qmldirContent) { int typeIndex = qmldirContent->indexOf(qmlType.toUtf8()); @@ -301,7 +286,7 @@ QString ContentLibraryBundleImporter::unimportComponent(const QString &qmlFile) } } - m_pendingTypes.insert(fullTypeName, false); + m_pendingTypes.insert(type, false); QVariantHash assetRefMap = loadAssetRefMap(bundleImportPath); bool writeAssetRefs = false; @@ -335,7 +320,7 @@ QString ContentLibraryBundleImporter::unimportComponent(const QString &qmlFile) auto doc = QmlDesignerPlugin::instance()->currentDesignDocument(); Model *model = doc ? doc->currentModel() : nullptr; if (model) { - Import import = Import::createLibraryImport(moduleName(), "1.0"); + Import import = Import::createLibraryImport(module, "1.0"); if (model->imports().contains(import)) model->changeImports({}, {import}); } @@ -348,14 +333,14 @@ QString ContentLibraryBundleImporter::unimportComponent(const QString &qmlFile) return {}; } -FilePath ContentLibraryBundleImporter::resolveBundleImportPath() +FilePath ContentLibraryBundleImporter::resolveBundleImportPath(const QString &bundleId) { FilePath bundleImportPath = QmlDesignerPlugin::instance()->documentManager() .generatedComponentUtils().componentBundlesBasePath(); if (bundleImportPath.isEmpty()) - return bundleImportPath; + return {}; - return bundleImportPath.resolvePath(m_bundleId); + return bundleImportPath.resolvePath(bundleId); } -} // namespace QmlDesigner::Internal +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.h index 7fb2a48886d..8155311e3e4 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.h @@ -3,59 +3,53 @@ #pragma once -#include +#include -#include "nodemetainfo.h" +#include #include #include -QT_BEGIN_NAMESPACE -QT_END_NAMESPACE +namespace QmlDesigner { -namespace QmlDesigner::Internal { +class NodeMetaInfo; class ContentLibraryBundleImporter : public QObject { Q_OBJECT public: - ContentLibraryBundleImporter(const QString &bundleDir, - const QString &bundleId, - const QStringList &sharedFiles, - QObject *parent = nullptr); + ContentLibraryBundleImporter(QObject *parent = nullptr); ~ContentLibraryBundleImporter() = default; - QString importComponent(const QString &qmlFile, + QString importComponent(const QString &bundleDir, const TypeName &type, const QString &qmlFile, const QStringList &files); - QString unimportComponent(const QString &qmlFile); - Utils::FilePath resolveBundleImportPath(); + QString unimportComponent(const TypeName &type, const QString &qmlFile); + Utils::FilePath resolveBundleImportPath(const QString &bundleId); signals: // The metaInfo parameter will be invalid if an error was encountered during // asynchronous part of the import. In this case all remaining pending imports have been // terminated, and will not receive separate importFinished notifications. #ifdef QDS_USE_PROJECTSTORAGE - void importFinished(const QmlDesigner::TypeName &typeName); + void importFinished(const QmlDesigner::TypeName &typeName, const QString &bundleId); #else - void importFinished(const QmlDesigner::NodeMetaInfo &metaInfo); + void importFinished(const QmlDesigner::NodeMetaInfo &metaInfo, const QString &bundleId); #endif - void unimportFinished(const QmlDesigner::NodeMetaInfo &metaInfo); + void unimportFinished(const QmlDesigner::NodeMetaInfo &metaInfo, const QString &bundleId); + void aboutToUnimport(const TypeName &type, const QString &bundleId); private: void handleImportTimer(); QVariantHash loadAssetRefMap(const Utils::FilePath &bundlePath); void writeAssetRefMap(const Utils::FilePath &bundlePath, const QVariantHash &assetRefMap); - QString moduleName(); - Utils::FilePath m_bundleDir; - QString m_bundleId; - QStringList m_sharedFiles; QTimer m_importTimer; int m_importTimerCount = 0; - bool m_importAddPending = false; + QString m_pendingImport; + QString m_bundleId; bool m_fullReset = false; - QHash m_pendingTypes; // + QHash m_pendingTypes; // }; -} // namespace QmlDesigner::Internal +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffect.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffect.cpp deleted file mode 100644 index f572fbe65f4..00000000000 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffect.cpp +++ /dev/null @@ -1,78 +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 "contentlibraryeffect.h" - -#include - -namespace QmlDesigner { - -ContentLibraryEffect::ContentLibraryEffect(QObject *parent, - const QString &name, - const QString &qml, - const TypeName &type, - const QUrl &icon, - const QStringList &files) - : QObject(parent), m_name(name), m_qml(qml), m_type(type), m_icon(icon), m_files(files) -{ - m_allFiles = m_files; - m_allFiles.push_back(m_qml); -} - -bool ContentLibraryEffect::filter(const QString &searchText) -{ - if (m_visible != m_name.contains(searchText, Qt::CaseInsensitive)) { - m_visible = !m_visible; - emit itemVisibleChanged(); - } - - return m_visible; -} - -QUrl ContentLibraryEffect::icon() const -{ - return m_icon; -} - -QString ContentLibraryEffect::qml() const -{ - return m_qml; -} - -TypeName ContentLibraryEffect::type() const -{ - return m_type; -} - -QStringList ContentLibraryEffect::files() const -{ - return m_files; -} - -bool ContentLibraryEffect::visible() const -{ - return m_visible; -} - -bool ContentLibraryEffect::setImported(bool imported) -{ - if (m_imported != imported) { - m_imported = imported; - emit itemImportedChanged(); - return true; - } - - return false; -} - -bool ContentLibraryEffect::imported() const -{ - return m_imported; -} - -QStringList ContentLibraryEffect::allFiles() const -{ - return m_allFiles; -} - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectscategory.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectscategory.cpp index 38e6eed3dad..f904775d52d 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectscategory.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectscategory.cpp @@ -3,14 +3,12 @@ #include "contentlibraryeffectscategory.h" -#include "contentlibraryeffect.h" - namespace QmlDesigner { ContentLibraryEffectsCategory::ContentLibraryEffectsCategory(QObject *parent, const QString &name) : QObject(parent), m_name(name) {} -void ContentLibraryEffectsCategory::addBundleItem(ContentLibraryEffect *bundleItem) +void ContentLibraryEffectsCategory::addBundleItem(ContentLibraryItem *bundleItem) { m_categoryItems.append(bundleItem); } @@ -19,7 +17,7 @@ bool ContentLibraryEffectsCategory::updateImportedState(const QStringList &impor { bool changed = false; - for (ContentLibraryEffect *item : std::as_const(m_categoryItems)) + for (ContentLibraryItem *item : std::as_const(m_categoryItems)) changed |= item->setImported(importedItems.contains(item->qml().chopped(4))); return changed; @@ -28,7 +26,7 @@ bool ContentLibraryEffectsCategory::updateImportedState(const QStringList &impor bool ContentLibraryEffectsCategory::filter(const QString &searchText) { bool visible = false; - for (ContentLibraryEffect *item : std::as_const(m_categoryItems)) + for (ContentLibraryItem *item : std::as_const(m_categoryItems)) visible |= item->filter(searchText); if (visible != m_visible) { @@ -55,7 +53,7 @@ bool ContentLibraryEffectsCategory::expanded() const return m_expanded; } -QList ContentLibraryEffectsCategory::categoryItems() const +QList ContentLibraryEffectsCategory::categoryItems() const { return m_categoryItems; } diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectscategory.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectscategory.h index 79737c1ec24..9f56de3c6b6 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectscategory.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectscategory.h @@ -3,12 +3,12 @@ #pragma once +#include "contentlibraryitem.h" + #include namespace QmlDesigner { -class ContentLibraryEffect; - class ContentLibraryEffectsCategory : public QObject { Q_OBJECT @@ -16,20 +16,20 @@ class ContentLibraryEffectsCategory : public QObject Q_PROPERTY(QString bundleCategoryName MEMBER m_name CONSTANT) Q_PROPERTY(bool bundleCategoryVisible MEMBER m_visible NOTIFY categoryVisibleChanged) Q_PROPERTY(bool bundleCategoryExpanded MEMBER m_expanded NOTIFY categoryExpandChanged) - Q_PROPERTY(QList bundleCategoryItems MEMBER m_categoryItems + Q_PROPERTY(QList bundleCategoryItems MEMBER m_categoryItems NOTIFY categoryItemsChanged) public: ContentLibraryEffectsCategory(QObject *parent, const QString &name); - void addBundleItem(ContentLibraryEffect *bundleItem); + void addBundleItem(ContentLibraryItem *bundleItem); bool updateImportedState(const QStringList &importedMats); bool filter(const QString &searchText); QString name() const; bool visible() const; bool expanded() const; - QList categoryItems() const; + QList categoryItems() const; signals: void categoryVisibleChanged(); @@ -41,7 +41,7 @@ private: bool m_visible = true; bool m_expanded = true; - QList m_categoryItems; + QList m_categoryItems; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.cpp index 334c017116c..e0fe2b6c54e 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.cpp @@ -4,8 +4,8 @@ #include "contentlibraryeffectsmodel.h" #include "contentlibrarybundleimporter.h" -#include "contentlibraryeffect.h" #include "contentlibraryeffectscategory.h" +#include "contentlibraryitem.h" #include "contentlibrarywidget.h" #include @@ -63,6 +63,11 @@ bool ContentLibraryEffectsModel::isValidIndex(int idx) const return idx > -1 && idx < rowCount(); } +QString ContentLibraryEffectsModel::bundleId() const +{ + return m_bundleId; +} + void ContentLibraryEffectsModel::updateIsEmpty() { bool anyCatVisible = Utils::anyOf(m_bundleCategories, [&](ContentLibraryEffectsCategory *cat) { @@ -88,49 +93,23 @@ QHash ContentLibraryEffectsModel::roleNames() const return roles; } -void ContentLibraryEffectsModel::createImporter(const QString &bundlePath, const QString &bundleId, - const QStringList &sharedFiles) -{ - m_importer = new Internal::ContentLibraryBundleImporter(bundlePath, bundleId, sharedFiles); -#ifdef QDS_USE_PROJECTSTORAGE - connect(m_importer, - &Internal::ContentLibraryBundleImporter::importFinished, - this, - [&](const QmlDesigner::TypeName &typeName) { - m_importerRunning = false; - emit importerRunningChanged(); - if (typeName.size()) - emit bundleItemImported(typeName); - }); -#else - connect(m_importer, - &Internal::ContentLibraryBundleImporter::importFinished, - this, - [&](const QmlDesigner::NodeMetaInfo &metaInfo) { - m_importerRunning = false; - emit importerRunningChanged(); - if (metaInfo.isValid()) - emit bundleItemImported(metaInfo); - }); -#endif - - connect(m_importer, &Internal::ContentLibraryBundleImporter::unimportFinished, this, - [&](const QmlDesigner::NodeMetaInfo &metaInfo) { - Q_UNUSED(metaInfo) - m_importerRunning = false; - emit importerRunningChanged(); - emit bundleItemUnimported(metaInfo); - }); - - resetModel(); - updateIsEmpty(); -} - void ContentLibraryEffectsModel::loadBundle() { - if (m_bundleExists || m_probeBundleDir) + auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils(); + + if (m_probeBundleDir || (m_bundleExists && m_bundleId == compUtils.effectsBundleId())) return; + // clean up + qDeleteAll(m_bundleCategories); + m_bundleCategories.clear(); + m_bundleExists = false; + m_isEmpty = true; + m_probeBundleDir = false; + m_bundleObj = {}; + m_bundleId.clear(); + m_bundlePath.clear(); + QDir bundleDir; if (!qEnvironmentVariable("EFFECT_BUNDLE_PATH").isEmpty()) @@ -145,30 +124,32 @@ void ContentLibraryEffectsModel::loadBundle() while (!bundleDir.cd("effect_bundle") && bundleDir.cdUp()) ; // do nothing - if (bundleDir.dirName() != "effect_bundle") // bundlePathDir not found + if (bundleDir.dirName() != "effect_bundle") { // bundlePathDir not found + resetModel(); return; + } } QString bundlePath = bundleDir.filePath("effect_bundle.json"); - if (m_bundleObj.isEmpty()) { - QFile propsFile(bundlePath); - - if (!propsFile.open(QIODevice::ReadOnly)) { - qWarning("Couldn't open effect_bundle.json"); - return; - } - - QJsonDocument bundleJsonDoc = QJsonDocument::fromJson(propsFile.readAll()); - if (bundleJsonDoc.isNull()) { - qWarning("Invalid effect_bundle.json file"); - return; - } else { - m_bundleObj = bundleJsonDoc.object(); - } + QFile bundleFile(bundlePath); + if (!bundleFile.open(QIODevice::ReadOnly)) { + qWarning("Couldn't open effect_bundle.json"); + resetModel(); + return; } - QString bundleId = m_bundleObj.value("id").toString(); + QJsonDocument bundleJsonDoc = QJsonDocument::fromJson(bundleFile.readAll()); + if (bundleJsonDoc.isNull()) { + qWarning("Invalid effect_bundle.json file"); + resetModel(); + return; + } + + m_bundleObj = bundleJsonDoc.object(); + + QString bundleType = compUtils.effectsBundleType(); + m_bundleId = compUtils.effectsBundleId(); const QJsonObject catsObj = m_bundleObj.value("categories").toObject(); const QStringList categories = catsObj.keys(); @@ -176,39 +157,36 @@ void ContentLibraryEffectsModel::loadBundle() auto category = new ContentLibraryEffectsCategory(this, cat); const QJsonObject itemsObj = catsObj.value(cat).toObject(); - const QStringList items = itemsObj.keys(); - for (const QString &item : items) { - const QJsonObject itemObj = itemsObj.value(item).toObject(); + const QStringList itemsNames = itemsObj.keys(); + for (const QString &itemName : itemsNames) { + const QJsonObject itemObj = itemsObj.value(itemName).toObject(); QStringList files; const QJsonArray assetsArr = itemObj.value("files").toArray(); - for (const auto /*QJson{Const,}ValueRef*/ &asset : assetsArr) + for (const QJsonValueConstRef &asset : assetsArr) files.append(asset.toString()); QUrl icon = QUrl::fromLocalFile(bundleDir.filePath(itemObj.value("icon").toString())); QString qml = itemObj.value("qml").toString(); - TypeName type = QLatin1String("%1.%2.%3") - .arg(QmlDesignerPlugin::instance()->documentManager() - .generatedComponentUtils().componentBundlesTypePrefix(), - bundleId, - qml.chopped(4)).toLatin1(); // chopped(4): remove .qml + TypeName type = QLatin1String("%1.%2") + .arg(bundleType, qml.chopped(4)).toLatin1(); // chopped(4): remove .qml - auto bundleItem = new ContentLibraryEffect(category, item, qml, type, icon, files); + auto bundleItem = new ContentLibraryItem(category, itemName, qml, type, icon, files); category->addBundleItem(bundleItem); } m_bundleCategories.append(category); } - QStringList sharedFiles; + m_bundleSharedFiles.clear(); const QJsonArray sharedFilesArr = m_bundleObj.value("sharedFiles").toArray(); - for (const auto /*QJson{Const,}ValueRef*/ &file : sharedFilesArr) - sharedFiles.append(file.toString()); - - createImporter(bundleDir.path(), bundleId, sharedFiles); + for (const QJsonValueConstRef &file : sharedFilesArr) + m_bundleSharedFiles.append(file.toString()); + m_bundlePath = bundleDir.path(); m_bundleExists = true; - emit bundleExistsChanged(); + updateIsEmpty(); + resetModel(); } bool ContentLibraryEffectsModel::hasRequiredQuick3DImport() const @@ -221,11 +199,6 @@ bool ContentLibraryEffectsModel::bundleExists() const return m_bundleExists; } -Internal::ContentLibraryBundleImporter *ContentLibraryEffectsModel::bundleImporter() const -{ - return m_importer; -} - void ContentLibraryEffectsModel::setSearchText(const QString &searchText) { QString lowerSearchText = searchText.toLower(); @@ -278,30 +251,26 @@ void ContentLibraryEffectsModel::resetModel() endResetModel(); } -void ContentLibraryEffectsModel::addInstance(ContentLibraryEffect *bundleItem) +void ContentLibraryEffectsModel::addInstance(ContentLibraryItem *bundleItem) { - QString err = m_importer->importComponent(bundleItem->qml(), bundleItem->files()); + QString err = m_widget->importer()->importComponent(m_bundlePath, bundleItem->type(), + bundleItem->qml(), + bundleItem->files() + m_bundleSharedFiles); - if (err.isEmpty()) { - m_importerRunning = true; - emit importerRunningChanged(); - } else { + if (err.isEmpty()) + m_widget->setImporterRunning(true); + else qWarning() << __FUNCTION__ << err; - } } -void ContentLibraryEffectsModel::removeFromProject(ContentLibraryEffect *bundleItem) +void ContentLibraryEffectsModel::removeFromProject(ContentLibraryItem *bundleItem) { - emit bundleItemAboutToUnimport(bundleItem->type()); + QString err = m_widget->importer()->unimportComponent(bundleItem->type(), bundleItem->qml()); - QString err = m_importer->unimportComponent(bundleItem->qml()); - - if (err.isEmpty()) { - m_importerRunning = true; - emit importerRunningChanged(); - } else { + if (err.isEmpty()) + m_widget->setImporterRunning(true); + else qWarning() << __FUNCTION__ << err; - } } } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.h index 5d67ac3da8b..5bcb857fca5 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.h @@ -3,22 +3,15 @@ #pragma once -#include "nodemetainfo.h" - #include -#include #include namespace QmlDesigner { -class ContentLibraryEffect; +class ContentLibraryItem; class ContentLibraryEffectsCategory; class ContentLibraryWidget; -namespace Internal { -class ContentLibraryBundleImporter; -} - class ContentLibraryEffectsModel : public QAbstractListModel { Q_OBJECT @@ -26,7 +19,6 @@ class ContentLibraryEffectsModel : public QAbstractListModel Q_PROPERTY(bool bundleExists READ bundleExists NOTIFY bundleExistsChanged) Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged) Q_PROPERTY(bool hasRequiredQuick3DImport READ hasRequiredQuick3DImport NOTIFY hasRequiredQuick3DImportChanged) - Q_PROPERTY(bool importerRunning MEMBER m_importerRunning NOTIFY importerRunningChanged) public: ContentLibraryEffectsModel(ContentLibraryWidget *parent = nullptr); @@ -49,46 +41,33 @@ public: void resetModel(); void updateIsEmpty(); - Internal::ContentLibraryBundleImporter *bundleImporter() const; + Q_INVOKABLE void addInstance(QmlDesigner::ContentLibraryItem *bundleItem); + Q_INVOKABLE void removeFromProject(QmlDesigner::ContentLibraryItem *bundleItem); - Q_INVOKABLE void addInstance(QmlDesigner::ContentLibraryEffect *bundleItem); - Q_INVOKABLE void removeFromProject(QmlDesigner::ContentLibraryEffect *bundleItem); + QString bundleId() const; signals: void isEmptyChanged(); void hasRequiredQuick3DImportChanged(); -#ifdef QDS_USE_PROJECTSTORAGE - void bundleItemImported(const QmlDesigner::TypeName &typeName); -#else - void bundleItemImported(const QmlDesigner::NodeMetaInfo &metaInfo); -#endif - void bundleItemAboutToUnimport(const QmlDesigner::TypeName &type); - void bundleItemUnimported(const QmlDesigner::NodeMetaInfo &metaInfo); - void importerRunningChanged(); void bundleExistsChanged(); private: bool isValidIndex(int idx) const; - void createImporter(const QString &bundlePath, const QString &bundleId, - const QStringList &sharedFiles); ContentLibraryWidget *m_widget = nullptr; QString m_searchText; + QString m_bundlePath; + QString m_bundleId; + QStringList m_bundleSharedFiles; QList m_bundleCategories; QJsonObject m_bundleObj; - Internal::ContentLibraryBundleImporter *m_importer = nullptr; bool m_isEmpty = true; bool m_bundleExists = false; bool m_probeBundleDir = false; - bool m_importerRunning = false; int m_quick3dMajorVersion = -1; int m_quick3dMinorVersion = -1; - - QString m_importerBundlePath; - QString m_importerBundleId; - QStringList m_importerSharedFiles; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryitem.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryitem.cpp new file mode 100644 index 00000000000..38fb69abbc2 --- /dev/null +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryitem.cpp @@ -0,0 +1,76 @@ +// 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 "contentlibraryitem.h" + +namespace QmlDesigner { + +ContentLibraryItem::ContentLibraryItem(QObject *parent, + const QString &name, + const QString &qml, + const TypeName &type, + const QUrl &icon, + const QStringList &files) + : QObject(parent), m_name(name), m_qml(qml), m_type(type), m_icon(icon), m_files(files) +{ + m_allFiles = m_files; + m_allFiles.push_back(m_qml); +} + +bool ContentLibraryItem::filter(const QString &searchText) +{ + if (m_visible != m_name.contains(searchText, Qt::CaseInsensitive)) { + m_visible = !m_visible; + emit itemVisibleChanged(); + } + + return m_visible; +} + +QUrl ContentLibraryItem::icon() const +{ + return m_icon; +} + +QString ContentLibraryItem::qml() const +{ + return m_qml; +} + +TypeName ContentLibraryItem::type() const +{ + return m_type; +} + +QStringList ContentLibraryItem::files() const +{ + return m_files; +} + +bool ContentLibraryItem::visible() const +{ + return m_visible; +} + +bool ContentLibraryItem::setImported(bool imported) +{ + if (m_imported != imported) { + m_imported = imported; + emit itemImportedChanged(); + return true; + } + + return false; +} + +bool ContentLibraryItem::imported() const +{ + return m_imported; +} + +QStringList ContentLibraryItem::allFiles() const +{ + return m_allFiles; +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffect.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryitem.h similarity index 74% rename from src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffect.h rename to src/plugins/qmldesigner/components/contentlibrary/contentlibraryitem.h index fdb302b6139..bdf87367281 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffect.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryitem.h @@ -10,7 +10,7 @@ namespace QmlDesigner { -class ContentLibraryEffect : public QObject +class ContentLibraryItem : public QObject { Q_OBJECT @@ -19,14 +19,15 @@ class ContentLibraryEffect : public QObject Q_PROPERTY(QStringList bundleItemFiles READ allFiles CONSTANT) Q_PROPERTY(bool bundleItemVisible MEMBER m_visible NOTIFY itemVisibleChanged) Q_PROPERTY(bool bundleItemImported READ imported WRITE setImported NOTIFY itemImportedChanged) + Q_PROPERTY(QString itemType MEMBER m_itemType CONSTANT) public: - ContentLibraryEffect(QObject *parent, - const QString &name, - const QString &qml, - const TypeName &type, - const QUrl &icon, - const QStringList &files); + ContentLibraryItem(QObject *parent, + const QString &name, + const QString &qml, + const TypeName &type, + const QUrl &icon, + const QStringList &files); bool filter(const QString &searchText); @@ -35,11 +36,9 @@ public: TypeName type() const; QStringList files() const; bool visible() const; - QString qmlFilePath() const; bool setImported(bool imported); bool imported() const; - QString parentDirPath() const; QStringList allFiles() const; signals: @@ -57,6 +56,7 @@ private: bool m_imported = false; QStringList m_allFiles; + const QString m_itemType = "item"; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.cpp index 834bc8aa30c..25d15231991 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.cpp @@ -32,6 +32,11 @@ bool ContentLibraryMaterial::filter(const QString &searchText) return m_visible; } +QString ContentLibraryMaterial::name() const +{ + return m_name; +} + QUrl ContentLibraryMaterial::icon() const { return m_icon; @@ -84,7 +89,7 @@ QString ContentLibraryMaterial::qmlFilePath() const return m_downloadPath + "/" + m_qml; } -QString ContentLibraryMaterial::parentDirPath() const +QString ContentLibraryMaterial::dirPath() const { return m_downloadPath; } diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.h index 55af2accbd3..aaaf9517f99 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.h @@ -17,9 +17,9 @@ class ContentLibraryMaterial : public QObject Q_PROPERTY(QString bundleMaterialName MEMBER m_name CONSTANT) Q_PROPERTY(QUrl bundleMaterialIcon MEMBER m_icon CONSTANT) Q_PROPERTY(bool bundleMaterialVisible MEMBER m_visible NOTIFY materialVisibleChanged) - Q_PROPERTY(bool bundleMaterialImported READ imported WRITE setImported NOTIFY materialImportedChanged) + Q_PROPERTY(bool bundleItemImported READ imported WRITE setImported NOTIFY materialImportedChanged) Q_PROPERTY(QString bundleMaterialBaseWebUrl MEMBER m_baseWebUrl CONSTANT) - Q_PROPERTY(QString bundleMaterialParentPath READ parentDirPath CONSTANT) + Q_PROPERTY(QString bundleMaterialDirPath READ dirPath CONSTANT) Q_PROPERTY(QStringList bundleMaterialFiles READ allFiles CONSTANT) Q_PROPERTY(QString itemType MEMBER m_itemType CONSTANT) @@ -37,6 +37,7 @@ public: Q_INVOKABLE bool isDownloaded() const; + QString name() const; QUrl icon() const; QString qml() const; TypeName type() const; @@ -46,7 +47,7 @@ public: bool setImported(bool imported); bool imported() const; - QString parentDirPath() const; + QString dirPath() const; QStringList allFiles() const; signals: diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp index 26747d359cd..adb47108a9a 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -40,7 +41,10 @@ ContentLibraryMaterialsModel::ContentLibraryMaterialsModel(ContentLibraryWidget qmlRegisterType("WebFetcher", 1, 0, "FileDownloader"); qmlRegisterType("WebFetcher", 1, 0, "MultiFileDownloader"); +} +void ContentLibraryMaterialsModel::loadBundle() +{ QDir bundleDir{m_downloadPath}; if (fetchBundleMetadata(bundleDir) && fetchBundleIcons(bundleDir)) loadMaterialBundle(bundleDir); @@ -192,11 +196,9 @@ void ContentLibraryMaterialsModel::downloadSharedFiles(const QDir &targetDir, co extractor->setAlwaysCreateDir(false); extractor->setClearTargetPathContents(false); - QObject::connect(extractor, &FileExtractor::finishedChanged, this, [this, downloader, extractor]() { + QObject::connect(extractor, &FileExtractor::finishedChanged, this, [downloader, extractor]() { downloader->deleteLater(); extractor->deleteLater(); - - createImporter(m_importerBundlePath, m_importerBundleId, m_importerSharedFiles); }); extractor->extract(); @@ -205,71 +207,48 @@ void ContentLibraryMaterialsModel::downloadSharedFiles(const QDir &targetDir, co downloader->start(); } -void ContentLibraryMaterialsModel::createImporter(const QString &bundlePath, const QString &bundleId, - const QStringList &sharedFiles) +QString ContentLibraryMaterialsModel::bundleId() const { - m_importer = new Internal::ContentLibraryBundleImporter(bundlePath, bundleId, sharedFiles); -#ifdef QDS_USE_PROJECTSTORAGE - connect(m_importer, - &Internal::ContentLibraryBundleImporter::importFinished, - this, - [&](const QmlDesigner::TypeName &typeName) { - m_importerRunning = false; - emit importerRunningChanged(); - if (typeName.size()) - emit bundleMaterialImported(typeName); - }); -#else - connect(m_importer, - &Internal::ContentLibraryBundleImporter::importFinished, - this, - [&](const QmlDesigner::NodeMetaInfo &metaInfo) { - m_importerRunning = false; - emit importerRunningChanged(); - if (metaInfo.isValid()) - emit bundleMaterialImported(metaInfo); - }); -#endif - - connect(m_importer, &Internal::ContentLibraryBundleImporter::unimportFinished, this, - [&](const QmlDesigner::NodeMetaInfo &metaInfo) { - Q_UNUSED(metaInfo) - m_importerRunning = false; - emit importerRunningChanged(); - emit bundleMaterialUnimported(metaInfo); - }); - - resetModel(); - updateIsEmpty(); + return m_bundleId; } -void ContentLibraryMaterialsModel::loadMaterialBundle(const QDir &matBundleDir) +void ContentLibraryMaterialsModel::loadMaterialBundle(const QDir &bundleDir) { - if (m_matBundleExists) + auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils(); + + if (m_bundleExists && m_bundleId == compUtils.materialsBundleId()) return; - QString matBundlePath = matBundleDir.filePath("material_bundle.json"); + // clean up + qDeleteAll(m_bundleCategories); + m_bundleCategories.clear(); + m_bundleExists = false; + m_isEmpty = true; + m_bundleObj = {}; + m_bundleId.clear(); - if (m_matBundleObj.isEmpty()) { - QFile matPropsFile(matBundlePath); + QString bundlePath = bundleDir.filePath("material_bundle.json"); - if (!matPropsFile.open(QIODevice::ReadOnly)) { - qWarning("Couldn't open material_bundle.json"); - return; - } - - QJsonDocument matBundleJsonDoc = QJsonDocument::fromJson(matPropsFile.readAll()); - if (matBundleJsonDoc.isNull()) { - qWarning("Invalid material_bundle.json file"); - return; - } else { - m_matBundleObj = matBundleJsonDoc.object(); - } + QFile bundleFile(bundlePath); + if (!bundleFile.open(QIODevice::ReadOnly)) { + qWarning("Couldn't open material_bundle.json"); + resetModel(); + return; } - QString bundleId = m_matBundleObj.value("id").toString(); + QJsonDocument bundleJsonDoc = QJsonDocument::fromJson(bundleFile.readAll()); + if (bundleJsonDoc.isNull()) { + qWarning("Invalid material_bundle.json file"); + resetModel(); + return; + } - const QJsonObject catsObj = m_matBundleObj.value("categories").toObject(); + m_bundleObj = bundleJsonDoc.object(); + + QString bundleType = compUtils.materialsBundleType(); + m_bundleId = compUtils.materialsBundleId(); + + const QJsonObject catsObj = m_bundleObj.value("categories").toObject(); const QStringList categories = catsObj.keys(); for (const QString &cat : categories) { auto category = new ContentLibraryMaterialsCategory(this, cat); @@ -281,16 +260,13 @@ void ContentLibraryMaterialsModel::loadMaterialBundle(const QDir &matBundleDir) QStringList files; const QJsonArray assetsArr = matObj.value("files").toArray(); - for (const auto /*QJson{Const,}ValueRef*/ &asset : assetsArr) + for (const QJsonValueConstRef &asset : assetsArr) files.append(asset.toString()); - QUrl icon = QUrl::fromLocalFile(matBundleDir.filePath(matObj.value("icon").toString())); + QUrl icon = QUrl::fromLocalFile(bundleDir.filePath(matObj.value("icon").toString())); QString qml = matObj.value("qml").toString(); - TypeName type = QLatin1String("%1.%2.%3") - .arg(QmlDesignerPlugin::instance()->documentManager() - .generatedComponentUtils().componentBundlesTypePrefix(), - bundleId, - qml.chopped(4)).toLatin1(); // chopped(4): remove .qml + TypeName type = QLatin1String("%1.%2") + .arg(bundleType, qml.chopped(4)).toLatin1(); // chopped(4): remove .qml auto bundleMat = new ContentLibraryMaterial(category, matName, qml, type, icon, files, m_downloadPath, m_baseUrl); @@ -300,30 +276,23 @@ void ContentLibraryMaterialsModel::loadMaterialBundle(const QDir &matBundleDir) m_bundleCategories.append(category); } - QStringList sharedFiles; - const QJsonArray sharedFilesArr = m_matBundleObj.value("sharedFiles").toArray(); - for (const auto /*QJson{Const,}ValueRef*/ &file : sharedFilesArr) - sharedFiles.append(file.toString()); + m_bundleSharedFiles.clear(); + const QJsonArray sharedFilesArr = m_bundleObj.value("sharedFiles").toArray(); + for (const QJsonValueConstRef &file : sharedFilesArr) + m_bundleSharedFiles.append(file.toString()); QStringList missingSharedFiles; - for (const QString &s : std::as_const(sharedFiles)) { - const QString fullSharedFilePath = matBundleDir.filePath(s); - - if (!QFileInfo::exists(fullSharedFilePath)) + for (const QString &s : std::as_const(m_bundleSharedFiles)) { + if (!QFileInfo::exists(bundleDir.filePath(s))) missingSharedFiles.push_back(s); } - if (missingSharedFiles.length() > 0) { - m_importerBundlePath = matBundleDir.path(); - m_importerBundleId = bundleId; - m_importerSharedFiles = sharedFiles; - downloadSharedFiles(matBundleDir, missingSharedFiles); - } else { - createImporter(matBundleDir.path(), bundleId, sharedFiles); - } + if (missingSharedFiles.length() > 0) + downloadSharedFiles(bundleDir, missingSharedFiles); - m_matBundleExists = true; - emit matBundleExistsChanged(); + m_bundleExists = true; + updateIsEmpty(); + resetModel(); } bool ContentLibraryMaterialsModel::hasRequiredQuick3DImport() const @@ -333,12 +302,7 @@ bool ContentLibraryMaterialsModel::hasRequiredQuick3DImport() const bool ContentLibraryMaterialsModel::matBundleExists() const { - return m_matBundleExists; -} - -Internal::ContentLibraryBundleImporter *ContentLibraryMaterialsModel::bundleImporter() const -{ - return m_importer; + return m_bundleExists; } void ContentLibraryMaterialsModel::setSearchText(const QString &searchText) @@ -360,11 +324,11 @@ void ContentLibraryMaterialsModel::setSearchText(const QString &searchText) updateIsEmpty(); } -void ContentLibraryMaterialsModel::updateImportedState(const QStringList &importedMats) +void ContentLibraryMaterialsModel::updateImportedState(const QStringList &importedItems) { bool changed = false; for (ContentLibraryMaterialsCategory *cat : std::as_const(m_bundleCategories)) - changed |= cat->updateImportedState(importedMats); + changed |= cat->updateImportedState(importedItems); if (changed) resetModel(); @@ -400,42 +364,23 @@ void ContentLibraryMaterialsModel::applyToSelected(ContentLibraryMaterial *mat, void ContentLibraryMaterialsModel::addToProject(ContentLibraryMaterial *mat) { - QString err = m_importer->importComponent(mat->qml(), mat->files()); + QString err = m_widget->importer()->importComponent(mat->dirPath(), mat->type(), mat->qml(), + mat->files() + m_bundleSharedFiles); - if (err.isEmpty()) { - m_importerRunning = true; - emit importerRunningChanged(); - } else { + if (err.isEmpty()) + m_widget->setImporterRunning(true); + else qWarning() << __FUNCTION__ << err; - } } void ContentLibraryMaterialsModel::removeFromProject(ContentLibraryMaterial *mat) { - emit bundleMaterialAboutToUnimport(mat->type()); + QString err = m_widget->importer()->unimportComponent(mat->type(), mat->qml()); - QString err = m_importer->unimportComponent(mat->qml()); - - if (err.isEmpty()) { - m_importerRunning = true; - emit importerRunningChanged(); - } else { + if (err.isEmpty()) + m_widget->setImporterRunning(true); + else qWarning() << __FUNCTION__ << err; - } -} - -bool ContentLibraryMaterialsModel::hasModelSelection() const -{ - return m_hasModelSelection; -} - -void ContentLibraryMaterialsModel::setHasModelSelection(bool b) -{ - if (b == m_hasModelSelection) - return; - - m_hasModelSelection = b; - emit hasModelSelectionChanged(); } } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.h index 21bd3741375..c0840dc3cc7 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.h @@ -3,22 +3,17 @@ #pragma once -#include "nodemetainfo.h" - #include -#include #include +QT_FORWARD_DECLARE_CLASS(QDir) + namespace QmlDesigner { class ContentLibraryMaterial; class ContentLibraryMaterialsCategory; class ContentLibraryWidget; -namespace Internal { -class ContentLibraryBundleImporter; -} - class ContentLibraryMaterialsModel : public QAbstractListModel { Q_OBJECT @@ -26,8 +21,6 @@ class ContentLibraryMaterialsModel : public QAbstractListModel Q_PROPERTY(bool matBundleExists READ matBundleExists NOTIFY matBundleExistsChanged) Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged) Q_PROPERTY(bool hasRequiredQuick3DImport READ hasRequiredQuick3DImport NOTIFY hasRequiredQuick3DImportChanged) - Q_PROPERTY(bool hasModelSelection READ hasModelSelection NOTIFY hasModelSelectionChanged) - Q_PROPERTY(bool importerRunning MEMBER m_importerRunning NOTIFY importerRunningChanged) public: ContentLibraryMaterialsModel(ContentLibraryWidget *parent = nullptr); @@ -38,40 +31,27 @@ public: QHash roleNames() const override; void setSearchText(const QString &searchText); - void updateImportedState(const QStringList &importedMats); - + void updateImportedState(const QStringList &importedItems); void setQuick3DImportVersion(int major, int minor); bool hasRequiredQuick3DImport() const; - bool matBundleExists() const; - bool hasModelSelection() const; - void setHasModelSelection(bool b); - void resetModel(); void updateIsEmpty(); - - Internal::ContentLibraryBundleImporter *bundleImporter() const; + void loadBundle(); Q_INVOKABLE void applyToSelected(QmlDesigner::ContentLibraryMaterial *mat, bool add = false); Q_INVOKABLE void addToProject(QmlDesigner::ContentLibraryMaterial *mat); Q_INVOKABLE void removeFromProject(QmlDesigner::ContentLibraryMaterial *mat); + QString bundleId() const; + signals: void isEmptyChanged(); void hasRequiredQuick3DImportChanged(); - void hasModelSelectionChanged(); void materialVisibleChanged(); void applyToSelectedTriggered(QmlDesigner::ContentLibraryMaterial *mat, bool add = false); -#ifdef QDS_USE_PROJECTSTORAGE - void bundleMaterialImported(const QmlDesigner::TypeName &typeName); -#else - void bundleMaterialImported(const QmlDesigner::NodeMetaInfo &metaInfo); -#endif - void bundleMaterialAboutToUnimport(const QmlDesigner::TypeName &type); - void bundleMaterialUnimported(const QmlDesigner::NodeMetaInfo &metaInfo); - void importerRunningChanged(); void matBundleExistsChanged(); private: @@ -80,29 +60,22 @@ private: bool fetchBundleMetadata(const QDir &bundleDir); bool isValidIndex(int idx) const; void downloadSharedFiles(const QDir &targetDir, const QStringList &files); - void createImporter(const QString &bundlePath, const QString &bundleId, - const QStringList &sharedFiles); ContentLibraryWidget *m_widget = nullptr; QString m_searchText; + QString m_bundleId; + QStringList m_bundleSharedFiles; QList m_bundleCategories; - QJsonObject m_matBundleObj; - Internal::ContentLibraryBundleImporter *m_importer = nullptr; + QJsonObject m_bundleObj; bool m_isEmpty = true; - bool m_matBundleExists = false; - bool m_hasModelSelection = false; - bool m_importerRunning = false; + bool m_bundleExists = false; int m_quick3dMajorVersion = -1; int m_quick3dMinorVersion = -1; QString m_downloadPath; QString m_baseUrl; - - QString m_importerBundlePath; - QString m_importerBundleId; - QStringList m_importerSharedFiles; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.cpp index 80dd7e816f8..d2c2df2baaf 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.cpp @@ -120,6 +120,11 @@ void ContentLibraryTexture::doSetDownloaded() m_toolTip = resolveToolTipText(); } +bool ContentLibraryTexture::visible() const +{ + return m_visible; +} + QString ContentLibraryTexture::parentDirPath() const { return m_dirPath; diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.h index 8f7197bc72e..7f5db6d7d60 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.h @@ -46,6 +46,8 @@ public: void setHasUpdate(bool value); bool hasUpdate() const; + bool visible() const; + signals: void textureVisibleChanged(); void textureToolTipChanged(); diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp index ed9723a1512..8dcf4575f78 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp @@ -4,7 +4,7 @@ #include "contentlibraryusermodel.h" #include "contentlibrarybundleimporter.h" -#include "contentlibraryeffect.h" +#include "contentlibraryitem.h" #include "contentlibrarymaterial.h" #include "contentlibrarymaterialscategory.h" #include "contentlibrarytexture.h" @@ -12,18 +12,16 @@ #include #include +#include #include +#include #include -#include #include -#include #include #include #include -#include -#include #include namespace QmlDesigner { @@ -32,10 +30,7 @@ ContentLibraryUserModel::ContentLibraryUserModel(ContentLibraryWidget *parent) : QAbstractListModel(parent) , m_widget(parent) { - m_userCategories = {tr("Materials"), tr("Textures")/*, tr("3D"), tr("Effects"), tr("2D components")*/}; // TODO - - loadMaterialBundle(); - loadTextureBundle(); + m_userCategories = {tr("Materials"), tr("Textures"), tr("3D"), /*tr("Effects"), tr("2D components")*/}; // TODO } int ContentLibraryUserModel::rowCount(const QModelIndex &) const @@ -52,18 +47,37 @@ QVariant ContentLibraryUserModel::data(const QModelIndex &index, int role) const return m_userCategories.at(index.row()); if (role == ItemsRole) { - if (index.row() == 0) + if (index.row() == MaterialsSectionIdx) return QVariant::fromValue(m_userMaterials); - if (index.row() == 1) + if (index.row() == TexturesSectionIdx) return QVariant::fromValue(m_userTextures); - if (index.row() == 2) + if (index.row() == Items3DSectionIdx) return QVariant::fromValue(m_user3DItems); - if (index.row() == 3) + if (index.row() == EffectsSectionIdx) return QVariant::fromValue(m_userEffects); } - if (role == VisibleRole) - return true; // TODO + if (role == NoMatchRole) { + if (index.row() == MaterialsSectionIdx) + return m_noMatchMaterials; + if (index.row() == TexturesSectionIdx) + return m_noMatchTextures; + if (index.row() == Items3DSectionIdx) + return m_noMatch3D; + if (index.row() == EffectsSectionIdx) + return m_noMatchEffects; + } + + if (role == VisibleRole) { + if (index.row() == MaterialsSectionIdx) + return !m_userMaterials.isEmpty(); + if (index.row() == TexturesSectionIdx) + return !m_userTextures.isEmpty(); + if (index.row() == Items3DSectionIdx) + return !m_user3DItems.isEmpty(); + if (index.row() == EffectsSectionIdx) + return !m_userEffects.isEmpty(); + } return {}; } @@ -73,29 +87,56 @@ bool ContentLibraryUserModel::isValidIndex(int idx) const return idx > -1 && idx < rowCount(); } -void ContentLibraryUserModel::updateIsEmpty() +void ContentLibraryUserModel::updateNoMatchMaterials() { - bool anyMatVisible = Utils::anyOf(m_userMaterials, [&](ContentLibraryMaterial *mat) { - return mat->visible(); + m_noMatchMaterials = Utils::allOf(m_userMaterials, [&](ContentLibraryMaterial *item) { + return !item->visible(); }); +} - bool newEmpty = !anyMatVisible || !m_widget->hasMaterialLibrary() || !hasRequiredQuick3DImport(); +void ContentLibraryUserModel::updateNoMatchTextures() +{ + m_noMatchTextures = Utils::allOf(m_userTextures, [&](ContentLibraryTexture *item) { + return !item->visible(); + }); +} - if (newEmpty != m_isEmpty) { - m_isEmpty = newEmpty; - emit isEmptyChanged(); - } +void ContentLibraryUserModel::updateNoMatch3D() +{ + m_noMatch3D = Utils::allOf(m_user3DItems, [&](ContentLibraryItem *item) { + return !item->visible(); + }); } void ContentLibraryUserModel::addMaterial(const QString &name, const QString &qml, const QUrl &icon, const QStringList &files) { - auto libMat = new ContentLibraryMaterial(this, name, qml, qmlToModule(qml), icon, files, - Paths::bundlesPathSetting().append("/User/materials")); + auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils(); + QString typePrefix = compUtils.userMaterialsBundleType(); + TypeName type = QLatin1String("%1.%2").arg(typePrefix, qml.chopped(4)).toLatin1(); + + auto libMat = new ContentLibraryMaterial(this, name, qml, type, icon, files, + Paths::bundlesPathSetting().append("/User/materials")); m_userMaterials.append(libMat); - int matSectionIdx = 0; - emit dataChanged(index(matSectionIdx), index(matSectionIdx)); + + emit dataChanged(index(MaterialsSectionIdx), index(MaterialsSectionIdx)); +} + +void ContentLibraryUserModel::add3DItem(const QString &name, const QString &qml, + const QUrl &icon, const QStringList &files) +{ + auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils(); + + QString typePrefix = compUtils.user3DBundleType(); + TypeName type = QLatin1String("%1.%2").arg(typePrefix, qml.chopped(4)).toLatin1(); + + m_user3DItems.append(new ContentLibraryItem(this, name, qml, type, icon, files)); +} + +void ContentLibraryUserModel::refresh3DSection() +{ + emit dataChanged(index(Items3DSectionIdx), index(Items3DSectionIdx)); } void ContentLibraryUserModel::addTextures(const QStringList &paths) @@ -117,51 +158,185 @@ void ContentLibraryUserModel::addTextures(const QStringList &paths) m_userTextures.append(tex); } - int texSectionIdx = 1; - emit dataChanged(index(texSectionIdx), index(texSectionIdx)); + emit dataChanged(index(TexturesSectionIdx), index(TexturesSectionIdx)); } -// returns unique library material's name and qml component -QPair ContentLibraryUserModel::getUniqueLibMaterialNameAndQml(const QString &matName) const +void ContentLibraryUserModel::add3DInstance(ContentLibraryItem *bundleItem) { - QTC_ASSERT(!m_bundleObj.isEmpty(), return {}); + QString err = m_widget->importer()->importComponent(m_bundlePath3D.path(), bundleItem->type(), + bundleItem->qml(), + bundleItem->files() + m_bundle3DSharedFiles); - const QJsonObject matsObj = m_bundleObj.value("materials").toObject(); - const QStringList matNames = matsObj.keys(); + if (err.isEmpty()) + m_widget->setImporterRunning(true); + else + qWarning() << __FUNCTION__ << err; +} - QStringList matQmls; - for (const QString &matName : matNames) - matQmls.append(matsObj.value(matName).toObject().value("qml").toString().chopped(4)); // remove .qml +void ContentLibraryUserModel::removeTexture(ContentLibraryTexture *tex) +{ + // remove resources + Utils::FilePath::fromString(tex->texturePath()).removeFile(); + Utils::FilePath::fromString(tex->iconPath()).removeFile(); - QString retName = matName.isEmpty() ? "Material" : matName; - retName = retName.trimmed(); + // remove from model + m_userTextures.removeOne(tex); + tex->deleteLater(); - QString retQml = retName; - retQml.remove(' '); - if (retQml.at(0).isLower()) - retQml[0] = retQml.at(0).toUpper(); - retQml.prepend("My"); + // update model + emit dataChanged(index(TexturesSectionIdx), index(TexturesSectionIdx)); +} - int num = 1; - if (matNames.contains(retName) || matQmls.contains(retQml)) { - while (matNames.contains(retName + QString::number(num)) - || matQmls.contains(retQml + QString::number(num))) { - ++num; +void ContentLibraryUserModel::removeFromContentLib(QObject *item) +{ + if (auto mat = qobject_cast(item)) + removeMaterialFromContentLib(mat); + else if (auto itm = qobject_cast(item)) + remove3DFromContentLib(itm); +} + +void ContentLibraryUserModel::removeMaterialFromContentLib(ContentLibraryMaterial *item) +{ + auto bundlePath = Utils::FilePath::fromString(Paths::bundlesPathSetting() + "/User/materials/"); + + QJsonArray itemsArr = m_bundleObjMaterial.value("items").toArray(); + + // remove qml and icon files + Utils::FilePath::fromString(item->qmlFilePath()).removeFile(); + Utils::FilePath::fromUrl(item->icon()).removeFile(); + + // remove from the bundle json file + for (int i = 0; i < itemsArr.size(); ++i) { + if (itemsArr.at(i).toObject().value("qml") == item->qml()) { + itemsArr.removeAt(i); + break; } + } + m_bundleObjMaterial.insert("items", itemsArr); - retName += QString::number(num); - retQml += QString::number(num); + auto result = bundlePath.pathAppended(Constants::BUNDLE_JSON_FILENAME) + .writeFileContents(QJsonDocument(m_bundleObjMaterial).toJson()); + if (!result) + qWarning() << __FUNCTION__ << result.error(); + + // delete dependency files if they are only used by the deleted material + QStringList allFiles; + for (const QJsonValueConstRef &itemRef : std::as_const(itemsArr)) + allFiles.append(itemRef.toObject().value("files").toVariant().toStringList()); + + const QStringList itemFiles = item->files(); + for (const QString &file : itemFiles) { + if (allFiles.count(file) == 0) // only used by the deleted item + bundlePath.pathAppended(file).removeFile(); } - return {retName, retQml + ".qml"}; + // remove from model + m_userMaterials.removeOne(item); + item->deleteLater(); + + // update model + emit dataChanged(index(MaterialsSectionIdx), index(MaterialsSectionIdx)); } -TypeName ContentLibraryUserModel::qmlToModule(const QString &qmlName) const +void ContentLibraryUserModel::remove3DFromContentLibByName(const QString &qmlFileName) { - return QLatin1String("%1.%2.%3").arg(QmlDesignerPlugin::instance()->documentManager() - .generatedComponentUtils().componentBundlesTypePrefix(), - m_bundleId, - qmlName.chopped(4)).toLatin1(); // chopped(4): remove .qml + ContentLibraryItem *itemToRemove = Utils::findOr(m_user3DItems, nullptr, + [&qmlFileName](ContentLibraryItem *item) { + return item->qml() == qmlFileName; + }); + + if (itemToRemove) + remove3DFromContentLib(itemToRemove); +} + +void ContentLibraryUserModel::remove3DFromContentLib(ContentLibraryItem *item) +{ + QJsonArray itemsArr = m_bundleObj3D.value("items").toArray(); + + // remove qml and icon files + m_bundlePath3D.pathAppended(item->qml()).removeFile(); + Utils::FilePath::fromUrl(item->icon()).removeFile(); + + // remove from the bundle json file + for (int i = 0; i < itemsArr.size(); ++i) { + if (itemsArr.at(i).toObject().value("qml") == item->qml()) { + itemsArr.removeAt(i); + break; + } + } + m_bundleObj3D.insert("items", itemsArr); + + auto result = m_bundlePath3D.pathAppended(Constants::BUNDLE_JSON_FILENAME) + .writeFileContents(QJsonDocument(m_bundleObj3D).toJson()); + if (!result) + qWarning() << __FUNCTION__ << result.error(); + + // delete dependency files if they are only used by the deleted item + QStringList allFiles; + for (const QJsonValueConstRef &itemRef : std::as_const(itemsArr)) + allFiles.append(itemRef.toObject().value("files").toVariant().toStringList()); + + const QStringList itemFiles = item->files(); + for (const QString &file : itemFiles) { + if (allFiles.count(file) == 0) // only used by the deleted item + m_bundlePath3D.pathAppended(file).removeFile(); + } + + // remove from model + m_user3DItems.removeOne(item); + item->deleteLater(); + + // update model + emit dataChanged(index(Items3DSectionIdx), index(Items3DSectionIdx)); +} + +/** + * @brief Gets unique Qml component and icon file material names from a given name + * @param defaultName input name + * @return file names + */ +QPair ContentLibraryUserModel::getUniqueLibMaterialNames(const QString &defaultName) const +{ + return getUniqueLibItemNames(defaultName, m_bundleObjMaterial); +} + +/** + * @brief Gets unique Qml component and icon file 3d item names from a given name + * @param defaultName input name + * @return file names + */ +QPair ContentLibraryUserModel::getUniqueLib3DNames(const QString &defaultName) const +{ + return getUniqueLibItemNames(defaultName, m_bundleObj3D); +} + +QPair ContentLibraryUserModel::getUniqueLibItemNames(const QString &defaultName, + const QJsonObject &bundleObj) const +{ + QTC_ASSERT(!bundleObj.isEmpty(), return {}); + + const QJsonArray itemsArr = bundleObj.value("items").toArray(); + + QStringList itemQmls, itemIcons; + for (const QJsonValueConstRef &itemRef : itemsArr) { + const QJsonObject &obj = itemRef.toObject(); + itemQmls.append(obj.value("qml").toString().chopped(4)); // remove .qml + itemIcons.append(QFileInfo(obj.value("icon").toString()).baseName()); + } + + QString baseQml = UniqueName::generateId(defaultName); + baseQml[0] = baseQml.at(0).toUpper(); + baseQml.prepend("My"); + + QString uniqueQml = UniqueName::generate(baseQml, [&] (const QString &name) { + return itemQmls.contains(name); + }); + + QString uniqueIcon = UniqueName::generate(defaultName, [&] (const QString &name) { + return itemIcons.contains(name); + }); + + return {uniqueQml + ".qml", uniqueIcon + ".png"}; } QHash ContentLibraryUserModel::roleNames() const @@ -169,121 +344,185 @@ QHash ContentLibraryUserModel::roleNames() const static const QHash roles { {NameRole, "categoryName"}, {VisibleRole, "categoryVisible"}, - {ItemsRole, "categoryItems"} + {ItemsRole, "categoryItems"}, + {NoMatchRole, "categoryNoMatch"} }; return roles; } -void ContentLibraryUserModel::createImporter(const QString &bundlePath, const QString &bundleId, - const QStringList &sharedFiles) +QJsonObject &ContentLibraryUserModel::bundleJsonMaterialObjectRef() { - m_importer = new Internal::ContentLibraryBundleImporter(bundlePath, bundleId, sharedFiles); -#ifdef QDS_USE_PROJECTSTORAGE - connect(m_importer, - &Internal::ContentLibraryBundleImporter::importFinished, - this, - [&](const QmlDesigner::TypeName &typeName) { - m_importerRunning = false; - emit importerRunningChanged(); - if (typeName.size()) - emit bundleMaterialImported(typeName); - }); -#else - connect(m_importer, - &Internal::ContentLibraryBundleImporter::importFinished, - this, - [&](const QmlDesigner::NodeMetaInfo &metaInfo) { - m_importerRunning = false; - emit importerRunningChanged(); - if (metaInfo.isValid()) - emit bundleMaterialImported(metaInfo); - }); -#endif - - connect(m_importer, &Internal::ContentLibraryBundleImporter::unimportFinished, this, - [&](const QmlDesigner::NodeMetaInfo &metaInfo) { - Q_UNUSED(metaInfo) - m_importerRunning = false; - emit importerRunningChanged(); - emit bundleMaterialUnimported(metaInfo); - }); - - resetModel(); - updateIsEmpty(); + return m_bundleObjMaterial; } -QJsonObject &ContentLibraryUserModel::bundleJsonObjectRef() +QJsonObject &ContentLibraryUserModel::bundleJson3DObjectRef() { - return m_bundleObj; + return m_bundleObj3D; +} + +void ContentLibraryUserModel::loadBundles() +{ + loadMaterialBundle(); + load3DBundle(); + loadTextureBundle(); } void ContentLibraryUserModel::loadMaterialBundle() { - if (m_matBundleExists) + auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils(); + if (m_matBundleExists && m_bundleIdMaterial == compUtils.userMaterialsBundleId()) return; - QDir bundleDir{Paths::bundlesPathSetting() + "/User/materials"}; - bundleDir.mkpath("."); + // clean up + qDeleteAll(m_userMaterials); + m_userMaterials.clear(); + m_matBundleExists = false; + m_noMatchMaterials = true; + m_bundleObjMaterial = {}; + m_bundleIdMaterial.clear(); - if (m_bundleObj.isEmpty()) { - auto jsonFilePath = Utils::FilePath::fromString(bundleDir.filePath("user_materials_bundle.json")); - if (!jsonFilePath.exists()) { - QString jsonContent = "{\n"; - jsonContent += " \"id\": \"UserMaterialBundle\",\n"; - jsonContent += " \"materials\": {\n"; - jsonContent += " }\n"; - jsonContent += "}"; - jsonFilePath.writeFileContents(jsonContent.toLatin1()); - } + m_bundlePathMaterial = Utils::FilePath::fromString(Paths::bundlesPathSetting() + "/User/materials"); + m_bundlePathMaterial.ensureWritableDir(); + m_bundlePathMaterial.pathAppended("icons").ensureWritableDir(); - QFile jsonFile(jsonFilePath.path()); - if (!jsonFile.open(QIODevice::ReadOnly)) { - qWarning("Couldn't open user_materials_bundle.json"); + auto jsonFilePath = m_bundlePathMaterial.pathAppended(Constants::BUNDLE_JSON_FILENAME); + if (!jsonFilePath.exists()) { + QString jsonContent = "{\n"; + jsonContent += " \"id\": \"UserMaterials\",\n"; + jsonContent += " \"items\": []\n"; + jsonContent += "}"; + Utils::expected_str res = jsonFilePath.writeFileContents(jsonContent.toLatin1()); + if (!res.has_value()) { + qWarning() << __FUNCTION__ << res.error(); + emit dataChanged(index(MaterialsSectionIdx), index(MaterialsSectionIdx)); return; } - - QJsonDocument matBundleJsonDoc = QJsonDocument::fromJson(jsonFile.readAll()); - if (matBundleJsonDoc.isNull()) { - qWarning("Invalid user_materials_bundle.json file"); - return; - } else { - m_bundleObj = matBundleJsonDoc.object(); - } } - m_bundleId = m_bundleObj.value("id").toString(); + Utils::expected_str jsonContents = jsonFilePath.fileContents(); + if (!jsonContents.has_value()) { + qWarning() << __FUNCTION__ << jsonContents.error(); + emit dataChanged(index(MaterialsSectionIdx), index(MaterialsSectionIdx)); + return; + } - // parse materials - const QJsonObject matsObj = m_bundleObj.value("materials").toObject(); - const QStringList materialNames = matsObj.keys(); - for (const QString &matName : materialNames) { - const QJsonObject matObj = matsObj.value(matName).toObject(); + QJsonDocument bundleJsonDoc = QJsonDocument::fromJson(jsonContents.value()); + if (bundleJsonDoc.isNull()) { + qWarning() << __FUNCTION__ << "Invalid json file" << jsonFilePath; + emit dataChanged(index(MaterialsSectionIdx), index(MaterialsSectionIdx)); + return; + } + m_bundleIdMaterial = compUtils.userMaterialsBundleId(); + m_bundleObjMaterial = bundleJsonDoc.object(); + m_bundleObjMaterial["id"] = m_bundleIdMaterial; + + // parse items + QString typePrefix = compUtils.userMaterialsBundleType(); + const QJsonArray itemsArr = m_bundleObjMaterial.value("items").toArray(); + for (const QJsonValueConstRef &itemRef : itemsArr) { + const QJsonObject itemObj = itemRef.toObject(); + + QString name = itemObj.value("name").toString(); + QString qml = itemObj.value("qml").toString(); + TypeName type = QLatin1String("%1.%2").arg(typePrefix, qml.chopped(4)).toLatin1(); + QUrl icon = m_bundlePathMaterial.pathAppended(itemObj.value("icon").toString()).toUrl(); QStringList files; - const QJsonArray assetsArr = matObj.value("files").toArray(); - for (const auto /*QJson{Const,}ValueRef*/ &asset : assetsArr) + const QJsonArray assetsArr = itemObj.value("files").toArray(); + for (const QJsonValueConstRef &asset : assetsArr) files.append(asset.toString()); - QUrl icon = QUrl::fromLocalFile(bundleDir.filePath(matObj.value("icon").toString())); - QString qml = matObj.value("qml").toString(); - - TypeName type = qmlToModule(qml); - - auto userMat = new ContentLibraryMaterial(this, matName, qml, type, icon, files, - bundleDir.path(), ""); - - m_userMaterials.append(userMat); + m_userMaterials.append(new ContentLibraryMaterial(this, name, qml, type, icon, files, + m_bundlePathMaterial.path(), "")); } - QStringList sharedFiles; - const QJsonArray sharedFilesArr = m_bundleObj.value("sharedFiles").toArray(); - for (const auto /*QJson{Const,}ValueRef*/ &file : sharedFilesArr) - sharedFiles.append(file.toString()); - - createImporter(bundleDir.path(), m_bundleId, sharedFiles); + m_bundleMaterialSharedFiles.clear(); + const QJsonArray sharedFilesArr = m_bundleObjMaterial.value("sharedFiles").toArray(); + for (const QJsonValueConstRef &file : sharedFilesArr) + m_bundleMaterialSharedFiles.append(file.toString()); m_matBundleExists = true; - emit matBundleExistsChanged(); + updateNoMatchMaterials(); + emit dataChanged(index(MaterialsSectionIdx), index(MaterialsSectionIdx)); +} + +void ContentLibraryUserModel::load3DBundle() +{ + auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils(); + + if (m_bundle3DExists && m_bundleId3D == compUtils.user3DBundleId()) + return; + + // clean up + qDeleteAll(m_user3DItems); + m_user3DItems.clear(); + m_bundle3DExists = false; + m_noMatch3D = true; + m_bundleObj3D = {}; + m_bundleId3D.clear(); + + m_bundlePath3D = Utils::FilePath::fromString(Paths::bundlesPathSetting() + "/User/3d"); + m_bundlePath3D.ensureWritableDir(); + m_bundlePath3D.pathAppended("icons").ensureWritableDir(); + + auto jsonFilePath = m_bundlePath3D.pathAppended(Constants::BUNDLE_JSON_FILENAME); + if (!jsonFilePath.exists()) { + QByteArray jsonContent = "{\n"; + jsonContent += " \"id\": \"User3D\",\n"; + jsonContent += " \"items\": []\n"; + jsonContent += "}"; + Utils::expected_str res = jsonFilePath.writeFileContents(jsonContent); + if (!res.has_value()) { + qWarning() << __FUNCTION__ << res.error(); + emit dataChanged(index(Items3DSectionIdx), index(Items3DSectionIdx)); + return; + } + } + + Utils::expected_str jsonContents = jsonFilePath.fileContents(); + if (!jsonContents.has_value()) { + qWarning() << __FUNCTION__ << jsonContents.error(); + emit dataChanged(index(Items3DSectionIdx), index(Items3DSectionIdx)); + return; + } + + QJsonDocument bundleJsonDoc = QJsonDocument::fromJson(jsonContents.value()); + if (bundleJsonDoc.isNull()) { + qWarning() << __FUNCTION__ << "Invalid json file" << jsonFilePath; + emit dataChanged(index(Items3DSectionIdx), index(Items3DSectionIdx)); + return; + } + + m_bundleId3D = compUtils.user3DBundleId(); + m_bundleObj3D = bundleJsonDoc.object(); + m_bundleObj3D["id"] = m_bundleId3D; + + // parse items + QString typePrefix = compUtils.user3DBundleType(); + const QJsonArray itemsArr = m_bundleObj3D.value("items").toArray(); + for (const QJsonValueConstRef &itemRef : itemsArr) { + const QJsonObject itemObj = itemRef.toObject(); + + QString name = itemObj.value("name").toString(); + QString qml = itemObj.value("qml").toString(); + TypeName type = QLatin1String("%1.%2").arg(typePrefix, qml.chopped(4)).toLatin1(); + QUrl icon = m_bundlePath3D.pathAppended(itemObj.value("icon").toString()).toUrl(); + QStringList files; + const QJsonArray assetsArr = itemObj.value("files").toArray(); + for (const QJsonValueConstRef &asset : assetsArr) + files.append(asset.toString()); + + m_user3DItems.append(new ContentLibraryItem(nullptr, name, qml, type, icon, files)); + } + + m_bundle3DSharedFiles.clear(); + const QJsonArray sharedFilesArr = m_bundleObj3D.value("sharedFiles").toArray(); + for (const QJsonValueConstRef &file : sharedFilesArr) + m_bundle3DSharedFiles.append(file.toString()); + + m_bundle3DExists = true; + updateNoMatch3D(); + emit dataChanged(index(Items3DSectionIdx), index(Items3DSectionIdx)); } void ContentLibraryUserModel::loadTextureBundle() @@ -308,8 +547,8 @@ void ContentLibraryUserModel::loadTextureBundle() m_userTextures.append(tex); } - int texSectionIdx = 1; - emit dataChanged(index(texSectionIdx), index(texSectionIdx)); + updateNoMatchTextures(); + emit dataChanged(index(TexturesSectionIdx), index(TexturesSectionIdx)); } bool ContentLibraryUserModel::hasRequiredQuick3DImport() const @@ -322,11 +561,6 @@ bool ContentLibraryUserModel::matBundleExists() const return m_matBundleExists; } -Internal::ContentLibraryBundleImporter *ContentLibraryUserModel::bundleImporter() const -{ - return m_importer; -} - void ContentLibraryUserModel::setSearchText(const QString &searchText) { QString lowerSearchText = searchText.toLower(); @@ -336,21 +570,39 @@ void ContentLibraryUserModel::setSearchText(const QString &searchText) m_searchText = lowerSearchText; - for (ContentLibraryMaterial *mat : std::as_const(m_userMaterials)) - mat->filter(m_searchText); + for (ContentLibraryMaterial *item : std::as_const(m_userMaterials)) + item->filter(m_searchText); - updateIsEmpty(); + for (ContentLibraryTexture *item : std::as_const(m_userTextures)) + item->filter(m_searchText); + + for (ContentLibraryItem *item : std::as_const(m_user3DItems)) + item->filter(m_searchText); + + updateNoMatchMaterials(); + updateNoMatchTextures(); + updateNoMatch3D(); + resetModel(); } -void ContentLibraryUserModel::updateImportedState(const QStringList &importedMats) +void ContentLibraryUserModel::updateMaterialsImportedState(const QStringList &importedItems) { bool changed = false; - for (ContentLibraryMaterial *mat : std::as_const(m_userMaterials)) - changed |= mat->setImported(importedMats.contains(mat->qml().chopped(4))); + changed |= mat->setImported(importedItems.contains(mat->qml().chopped(4))); if (changed) - resetModel(); + emit dataChanged(index(MaterialsSectionIdx), index(MaterialsSectionIdx)); +} + +void ContentLibraryUserModel::update3DImportedState(const QStringList &importedItems) +{ + bool changed = false; + for (ContentLibraryItem *item : std::as_const(m_user3DItems)) + changed |= item->setImported(importedItems.contains(item->qml().chopped(4))); + + if (changed) + emit dataChanged(index(Items3DSectionIdx), index(Items3DSectionIdx)); } void ContentLibraryUserModel::setQuick3DImportVersion(int major, int minor) @@ -366,8 +618,6 @@ void ContentLibraryUserModel::setQuick3DImportVersion(int major, int minor) return; emit hasRequiredQuick3DImportChanged(); - - updateIsEmpty(); } void ContentLibraryUserModel::resetModel() @@ -381,44 +631,56 @@ void ContentLibraryUserModel::applyToSelected(ContentLibraryMaterial *mat, bool emit applyToSelectedTriggered(mat, add); } -void ContentLibraryUserModel::addToProject(ContentLibraryMaterial *mat) +void ContentLibraryUserModel::addToProject(QObject *item) { - QString err = m_importer->importComponent(mat->qml(), mat->files()); - - if (err.isEmpty()) { - m_importerRunning = true; - emit importerRunningChanged(); + QString bundleDir; + TypeName type; + QString qmlFile; + QStringList files; + if (auto mat = qobject_cast(item)) { + bundleDir = mat->dirPath(); + type = mat->type(); + qmlFile = mat->qml(); + files = mat->files() + m_bundleMaterialSharedFiles; + } else if (auto itm = qobject_cast(item)) { + bundleDir = m_bundlePath3D.toString(); + type = itm->type(); + qmlFile = itm->qml(); + files = itm->files() + m_bundle3DSharedFiles; } else { - qWarning() << __FUNCTION__ << err; - } -} - -void ContentLibraryUserModel::removeFromProject(ContentLibraryMaterial *mat) -{ - emit bundleMaterialAboutToUnimport(mat->type()); - - QString err = m_importer->unimportComponent(mat->qml()); - - if (err.isEmpty()) { - m_importerRunning = true; - emit importerRunningChanged(); - } else { - qWarning() << __FUNCTION__ << err; - } -} - -bool ContentLibraryUserModel::hasModelSelection() const -{ - return m_hasModelSelection; -} - -void ContentLibraryUserModel::setHasModelSelection(bool b) -{ - if (b == m_hasModelSelection) + qWarning() << __FUNCTION__ << "Unsupported Item"; return; + } - m_hasModelSelection = b; - emit hasModelSelectionChanged(); + QString err = m_widget->importer()->importComponent(bundleDir, type, qmlFile, files); + + if (err.isEmpty()) + m_widget->setImporterRunning(true); + else + qWarning() << __FUNCTION__ << err; +} + +void ContentLibraryUserModel::removeFromProject(QObject *item) +{ + TypeName type; + QString qml; + if (auto mat = qobject_cast(item)) { + type = mat->type(); + qml = mat->qml(); + } else if (auto itm = qobject_cast(item)) { + type = itm->type(); + qml = itm->qml(); + } else { + qWarning() << __FUNCTION__ << "Unsupported Item"; + return; + } + + QString err = m_widget->importer()->unimportComponent(type, qml); + + if (err.isEmpty()) + m_widget->setImporterRunning(true); + else + qWarning() << __FUNCTION__ << err; } } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h index 3e9a96fd9d7..2a7f9a66f30 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h @@ -3,7 +3,7 @@ #pragma once -#include "modelfwd.h" +#include #include #include @@ -12,29 +12,23 @@ QT_FORWARD_DECLARE_CLASS(QUrl) namespace QmlDesigner { -class ContentLibraryEffect; +class ContentLibraryItem; class ContentLibraryMaterial; class ContentLibraryTexture; class ContentLibraryWidget; class NodeMetaInfo; -namespace Internal { -class ContentLibraryBundleImporter; -} - class ContentLibraryUserModel : public QAbstractListModel { Q_OBJECT Q_PROPERTY(bool matBundleExists READ matBundleExists NOTIFY matBundleExistsChanged) - Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged) + Q_PROPERTY(bool bundle3DExists MEMBER m_bundle3DExists NOTIFY bundle3DExistsChanged) Q_PROPERTY(bool hasRequiredQuick3DImport READ hasRequiredQuick3DImport NOTIFY hasRequiredQuick3DImportChanged) - Q_PROPERTY(bool hasModelSelection READ hasModelSelection NOTIFY hasModelSelectionChanged) - Q_PROPERTY(bool importerRunning MEMBER m_importerRunning NOTIFY importerRunningChanged) Q_PROPERTY(QList userMaterials MEMBER m_userMaterials NOTIFY userMaterialsChanged) Q_PROPERTY(QList userTextures MEMBER m_userTextures NOTIFY userTexturesChanged) - Q_PROPERTY(QList user3DItems MEMBER m_user3DItems NOTIFY user3DItemsChanged) - Q_PROPERTY(QList userEffects MEMBER m_userEffects NOTIFY userEffectsChanged) + Q_PROPERTY(QList user3DItems MEMBER m_user3DItems NOTIFY user3DItemsChanged) + Q_PROPERTY(QList userEffects MEMBER m_userEffects NOTIFY userEffectsChanged) public: ContentLibraryUserModel(ContentLibraryWidget *parent = nullptr); @@ -44,10 +38,11 @@ public: QHash roleNames() const override; void setSearchText(const QString &searchText); - void updateImportedState(const QStringList &importedMats); + void updateMaterialsImportedState(const QStringList &importedItems); + void update3DImportedState(const QStringList &importedItems); - QPair getUniqueLibMaterialNameAndQml(const QString &matName) const; - TypeName qmlToModule(const QString &qmlName) const; + QPair getUniqueLibMaterialNames(const QString &defaultName = "Material") const; + QPair getUniqueLib3DNames(const QString &defaultName = "Item") const; void setQuick3DImportVersion(int major, int minor); @@ -55,78 +50,86 @@ public: bool matBundleExists() const; - bool hasModelSelection() const; - void setHasModelSelection(bool b); - void resetModel(); - void updateIsEmpty(); + void updateNoMatchMaterials(); + void updateNoMatchTextures(); + void updateNoMatch3D(); void addMaterial(const QString &name, const QString &qml, const QUrl &icon, const QStringList &files); + void add3DItem(const QString &name, const QString &qml, const QUrl &icon, const QStringList &files); + void refresh3DSection(); void addTextures(const QStringList &paths); - void setBundleObj(const QJsonObject &newBundleObj); - QJsonObject &bundleJsonObjectRef(); + void add3DInstance(ContentLibraryItem *bundleItem); - Internal::ContentLibraryBundleImporter *bundleImporter() const; + void remove3DFromContentLibByName(const QString &qmlFileName); + + void setBundleObj(const QJsonObject &newBundleObj); + QJsonObject &bundleJsonMaterialObjectRef(); + QJsonObject &bundleJson3DObjectRef(); + + void loadBundles(); Q_INVOKABLE void applyToSelected(QmlDesigner::ContentLibraryMaterial *mat, bool add = false); - Q_INVOKABLE void addToProject(QmlDesigner::ContentLibraryMaterial *mat); - Q_INVOKABLE void removeFromProject(QmlDesigner::ContentLibraryMaterial *mat); + Q_INVOKABLE void addToProject(QObject *item); + Q_INVOKABLE void removeFromProject(QObject *item); + Q_INVOKABLE void removeTexture(QmlDesigner::ContentLibraryTexture *tex); + Q_INVOKABLE void removeFromContentLib(QObject *item); signals: - void isEmptyChanged(); void hasRequiredQuick3DImportChanged(); - void hasModelSelectionChanged(); void userMaterialsChanged(); void userTexturesChanged(); void user3DItemsChanged(); void userEffectsChanged(); - void applyToSelectedTriggered(QmlDesigner::ContentLibraryMaterial *mat, bool add = false); - -#ifdef QDS_USE_PROJECTSTORAGE - void bundleMaterialImported(const QmlDesigner::TypeName &typeName); -#else - void bundleMaterialImported(const QmlDesigner::NodeMetaInfo &metaInfo); -#endif - void bundleMaterialAboutToUnimport(const QmlDesigner::TypeName &type); - void bundleMaterialUnimported(const QmlDesigner::NodeMetaInfo &metaInfo); - void importerRunningChanged(); void matBundleExistsChanged(); + void bundle3DExistsChanged(); private: + enum SectionIndex { MaterialsSectionIdx = 0, + TexturesSectionIdx, + Items3DSectionIdx, + EffectsSectionIdx }; + void loadMaterialBundle(); + void load3DBundle(); void loadTextureBundle(); bool isValidIndex(int idx) const; - void createImporter(const QString &bundlePath, const QString &bundleId, - const QStringList &sharedFiles); + void removeMaterialFromContentLib(ContentLibraryMaterial *mat); + void remove3DFromContentLib(ContentLibraryItem *item); + QPair getUniqueLibItemNames(const QString &defaultName, + const QJsonObject &bundleObj) const; ContentLibraryWidget *m_widget = nullptr; QString m_searchText; - QString m_bundleId; + QString m_bundleIdMaterial; + QString m_bundleId3D; + QStringList m_bundleMaterialSharedFiles; + QStringList m_bundle3DSharedFiles; + Utils::FilePath m_bundlePathMaterial; + Utils::FilePath m_bundlePath3D; QList m_userMaterials; QList m_userTextures; - QList m_userEffects; - QList m_user3DItems; + QList m_userEffects; + QList m_user3DItems; QStringList m_userCategories; - QJsonObject m_bundleObj; - Internal::ContentLibraryBundleImporter *m_importer = nullptr; + QJsonObject m_bundleObjMaterial; + QJsonObject m_bundleObj3D; - bool m_isEmpty = true; + bool m_noMatchMaterials = true; + bool m_noMatchTextures = true; + bool m_noMatch3D = true; + bool m_noMatchEffects = true; bool m_matBundleExists = false; - bool m_hasModelSelection = false; - bool m_importerRunning = false; + bool m_bundle3DExists = false; int m_quick3dMajorVersion = -1; int m_quick3dMinorVersion = -1; - QString m_importerBundlePath; - QString m_importerBundleId; - QStringList m_importerSharedFiles; - - enum Roles { NameRole = Qt::UserRole + 1, VisibleRole, ItemsRole }; + enum Roles { NameRole = Qt::UserRole + 1, VisibleRole, ItemsRole, NoMatchRole }; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp index dd8a4d9919e..9258faaf33c 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp @@ -3,10 +3,8 @@ #include "contentlibraryview.h" -#include "asset.h" -#include "bindingproperty.h" #include "contentlibrarybundleimporter.h" -#include "contentlibraryeffect.h" +#include "contentlibraryitem.h" #include "contentlibraryeffectsmodel.h" #include "contentlibrarymaterial.h" #include "contentlibrarymaterialsmodel.h" @@ -14,18 +12,21 @@ #include "contentlibrarytexturesmodel.h" #include "contentlibraryusermodel.h" #include "contentlibrarywidget.h" -#include "documentmanager.h" -#include "externaldependenciesinterface.h" -#include "nodelistproperty.h" -#include "qmldesignerconstants.h" -#include "qmlobjectnode.h" -#include "variantproperty.h" -#include "utils3d.h" +#include +#include #include - -#include +#include #include +#include +#include +#include +#include +#include +#include +#include +#include + #include #ifndef QMLDESIGNER_TEST @@ -39,13 +40,16 @@ #include #include #include +#include #include #include namespace QmlDesigner { -ContentLibraryView::ContentLibraryView(ExternalDependenciesInterface &externalDependencies) +ContentLibraryView::ContentLibraryView(AsynchronousImageCache &imageCache, + ExternalDependenciesInterface &externalDependencies) : AbstractView(externalDependencies) + , m_imageCache(imageCache) , m_createTexture(this) {} @@ -70,9 +74,9 @@ WidgetInfo ContentLibraryView::widgetInfo() [&] (QmlDesigner::ContentLibraryTexture *tex) { m_draggedBundleTexture = tex; }); - connect(m_widget, &ContentLibraryWidget::bundleEffectDragStarted, this, - [&] (QmlDesigner::ContentLibraryEffect *eff) { - m_draggedBundleEffect = eff; + connect(m_widget, &ContentLibraryWidget::bundleItemDragStarted, this, + [&] (QmlDesigner::ContentLibraryItem *item) { + m_draggedBundleItem = item; }); connect(m_widget, &ContentLibraryWidget::addTextureRequested, this, @@ -89,133 +93,41 @@ WidgetInfo ContentLibraryView::widgetInfo() m_widget->environmentsModel()->setHasSceneEnv(sceneEnvExists); }); - ContentLibraryMaterialsModel *materialsModel = m_widget->materialsModel().data(); - - connect(materialsModel, + connect(m_widget->materialsModel(), &ContentLibraryMaterialsModel::applyToSelectedTriggered, this, [&](ContentLibraryMaterial *bundleMat, bool add) { - if (m_selectedModels.isEmpty()) - return; + if (m_selectedModels.isEmpty()) + return; - m_bundleMaterialTargets = m_selectedModels; - m_bundleMaterialAddToSelected = add; + m_bundleMaterialTargets = m_selectedModels; + m_bundleMaterialAddToSelected = add; - ModelNode defaultMat = getBundleMaterialDefaultInstance(bundleMat->type()); - if (defaultMat.isValid()) - applyBundleMaterialToDropTarget(defaultMat); - else - m_widget->materialsModel()->addToProject(bundleMat); - }); - -#ifdef QDS_USE_PROJECTSTORAGE - connect(materialsModel, - &ContentLibraryMaterialsModel::bundleMaterialImported, - this, - [&](const QmlDesigner::TypeName &typeName) { - applyBundleMaterialToDropTarget({}, typeName); - updateBundleMaterialsImportedState(); - }); -#else - connect(materialsModel, - &ContentLibraryMaterialsModel::bundleMaterialImported, - this, - [&](const QmlDesigner::NodeMetaInfo &metaInfo) { - applyBundleMaterialToDropTarget({}, metaInfo); - updateBundleMaterialsImportedState(); - }); -#endif - - connect(materialsModel, &ContentLibraryMaterialsModel::bundleMaterialAboutToUnimport, this, - [&] (const QmlDesigner::TypeName &type) { - // delete instances of the bundle material that is about to be unimported - executeInTransaction("ContentLibraryView::widgetInfo", [&] { - ModelNode matLib = Utils3D::materialLibraryNode(this); - if (!matLib.isValid()) - return; - - Utils::reverseForeach(matLib.directSubModelNodes(), [&](const ModelNode &mat) { - if (mat.isValid() && mat.type() == type) - QmlObjectNode(mat).destroy(); - }); - }); + ModelNode defaultMat = getBundleMaterialDefaultInstance(bundleMat->type()); + if (defaultMat.isValid()) + applyBundleMaterialToDropTarget(defaultMat); + else + m_widget->materialsModel()->addToProject(bundleMat); }); - connect(materialsModel, &ContentLibraryMaterialsModel::bundleMaterialUnimported, this, - &ContentLibraryView::updateBundleMaterialsImportedState); - - ContentLibraryEffectsModel *effectsModel = m_widget->effectsModel().data(); - -#ifdef QDS_USE_PROJECTSTORAGE - connect(effectsModel, - &ContentLibraryEffectsModel::bundleItemImported, + connect(m_widget->userModel(), + &ContentLibraryUserModel::applyToSelectedTriggered, this, - [&](const QmlDesigner::TypeName &typeName) { - QTC_ASSERT(typeName.size(), return); + [&](ContentLibraryMaterial *bundleMat, bool add) { + if (m_selectedModels.isEmpty()) + return; - if (!m_bundleEffectTarget) - m_bundleEffectTarget = Utils3D::active3DSceneNode(this); + m_bundleMaterialTargets = m_selectedModels; + m_bundleMaterialAddToSelected = add; - QTC_ASSERT(m_bundleEffectTarget, return); + ModelNode defaultMat = getBundleMaterialDefaultInstance(bundleMat->type()); + if (defaultMat.isValid()) + applyBundleMaterialToDropTarget(defaultMat); + else + m_widget->userModel()->addToProject(bundleMat); + }); - executeInTransaction("ContentLibraryView::widgetInfo", [&] { - QVector3D pos = m_bundleEffectPos.value(); - ModelNode newEffNode = createModelNode( - typeName, -1, -1, {{"x", pos.x()}, {"y", pos.y()}, {"z", pos.z()}}); - m_bundleEffectTarget.defaultNodeListProperty().reparentHere(newEffNode); - clearSelectedModelNodes(); - selectModelNode(newEffNode); - }); - - updateBundleEffectsImportedState(); - m_bundleEffectTarget = {}; - m_bundleEffectPos = {}; - }); -#else - connect(effectsModel, - &ContentLibraryEffectsModel::bundleItemImported, - this, - [&](const QmlDesigner::NodeMetaInfo &metaInfo) { - QTC_ASSERT(metaInfo.isValid(), return); - - if (!m_bundleEffectTarget) - m_bundleEffectTarget = Utils3D::active3DSceneNode(this); - - QTC_ASSERT(m_bundleEffectTarget, return); - - executeInTransaction("ContentLibraryView::widgetInfo", [&] { - QVector3D pos = m_bundleEffectPos.value(); - ModelNode newEffNode = createModelNode(metaInfo.typeName(), - metaInfo.majorVersion(), - metaInfo.minorVersion(), - {{"x", pos.x()}, - {"y", pos.y()}, - {"z", pos.z()}}); - m_bundleEffectTarget.defaultNodeListProperty().reparentHere(newEffNode); - clearSelectedModelNodes(); - selectModelNode(newEffNode); - }); - - updateBundleEffectsImportedState(); - m_bundleEffectTarget = {}; - m_bundleEffectPos = {}; - }); -#endif - connect(effectsModel, &ContentLibraryEffectsModel::bundleItemAboutToUnimport, this, - [&] (const QmlDesigner::TypeName &type) { - // delete instances of the bundle effect that is about to be unimported - executeInTransaction("ContentLibraryView::widgetInfo", [&] { - NodeMetaInfo metaInfo = model()->metaInfo(type); - QList effects = allModelNodesOfType(metaInfo); - for (ModelNode &eff : effects) - eff.destroy(); - }); - }); - - connect(effectsModel, &ContentLibraryEffectsModel::bundleItemUnimported, this, - &ContentLibraryView::updateBundleEffectsImportedState); - - connectUserBundle(); + connectImporter(); } return createWidgetInfo(m_widget.data(), @@ -225,62 +137,106 @@ WidgetInfo ContentLibraryView::widgetInfo() tr("Content Library")); } -void ContentLibraryView::connectUserBundle() +void ContentLibraryView::connectImporter() { - ContentLibraryUserModel *userModel = m_widget->userModel().data(); - - connect(userModel, - &ContentLibraryUserModel::applyToSelectedTriggered, - this, - [&](ContentLibraryMaterial *bundleMat, bool add) { - if (m_selectedModels.isEmpty()) - return; - - m_bundleMaterialTargets = m_selectedModels; - m_bundleMaterialAddToSelected = add; - - ModelNode defaultMat = getBundleMaterialDefaultInstance(bundleMat->type()); - if (defaultMat.isValid()) - applyBundleMaterialToDropTarget(defaultMat); - else - m_widget->userModel()->addToProject(bundleMat); - }); - #ifdef QDS_USE_PROJECTSTORAGE - connect(userModel, - &ContentLibraryUserModel::bundleMaterialImported, + connect(m_widget->importer(), + &ContentLibraryBundleImporter::importFinished, this, - [&](const QmlDesigner::TypeName &typeName) { - applyBundleMaterialToDropTarget({}, typeName); - updateBundleUserMaterialsImportedState(); + [&](const QmlDesigner::TypeName &typeName, const QString &bundleId) { + QTC_ASSERT(typeName.size(), return); + if (isMaterialBundle(bundleId)) { + applyBundleMaterialToDropTarget({}, typeName); + } else if (isItemBundle(bundleId)) { + if (!m_bundleItemTarget) + m_bundleItemTarget = Utils3D::active3DSceneNode(this); + + QTC_ASSERT(m_bundleItemTarget, return); + + executeInTransaction("ContentLibraryView::widgetInfo", [&] { + QVector3D pos = m_bundleItemPos.value(); + ModelNode newNode = createModelNode( + typeName, -1, -1, {{"x", pos.x()}, {"y", pos.y()}, {"z", pos.z()}}); + m_bundleItemTarget.defaultNodeListProperty().reparentHere(newNode); + clearSelectedModelNodes(); + selectModelNode(newNode); + }); + + m_bundleItemTarget = {}; + m_bundleItemPos = {}; + } }); #else - connect(userModel, - &ContentLibraryUserModel::bundleMaterialImported, + connect(m_widget->importer(), + &ContentLibraryBundleImporter::importFinished, this, - [&](const QmlDesigner::NodeMetaInfo &metaInfo) { - applyBundleMaterialToDropTarget({}, metaInfo); - updateBundleUserMaterialsImportedState(); + [&](const QmlDesigner::NodeMetaInfo &metaInfo, const QString &bundleId) { + QTC_ASSERT(metaInfo.isValid(), return); + if (isMaterialBundle(bundleId)) { + applyBundleMaterialToDropTarget({}, metaInfo); + } else if (isItemBundle(bundleId)) { + if (!m_bundleItemTarget) + m_bundleItemTarget = Utils3D::active3DSceneNode(this); + + QTC_ASSERT(m_bundleItemTarget, return); + + executeInTransaction("ContentLibraryView::connectImporter", [&] { + QVector3D pos = m_bundleItemPos.value(); + ModelNode newNode = createModelNode(metaInfo.typeName(), + metaInfo.majorVersion(), + metaInfo.minorVersion(), + {{"x", pos.x()}, + {"y", pos.y()}, + {"z", pos.z()}}); + m_bundleItemTarget.defaultNodeListProperty().reparentHere(newNode); + clearSelectedModelNodes(); + selectModelNode(newNode); + }); + + m_bundleItemTarget = {}; + m_bundleItemPos = {}; + } }); #endif - connect(userModel, &ContentLibraryUserModel::bundleMaterialAboutToUnimport, this, - [&] (const QmlDesigner::TypeName &type) { - // delete instances of the bundle material that is about to be unimported - executeInTransaction("ContentLibraryView::connectUserModel", [&] { - ModelNode matLib = Utils3D::materialLibraryNode(this); - if (!matLib.isValid()) - return; + connect(m_widget->importer(), &ContentLibraryBundleImporter::aboutToUnimport, this, + [&] (const QmlDesigner::TypeName &type, const QString &bundleId) { + if (isMaterialBundle(bundleId)) { + // delete instances of the bundle material that is about to be unimported + executeInTransaction("ContentLibraryView::connectImporter", [&] { + ModelNode matLib = Utils3D::materialLibraryNode(this); + if (!matLib.isValid()) + return; - Utils::reverseForeach(matLib.directSubModelNodes(), [&](const ModelNode &mat) { - if (mat.isValid() && mat.type() == type) - QmlObjectNode(mat).destroy(); - }); + Utils::reverseForeach(matLib.directSubModelNodes(), [&](const ModelNode &mat) { + if (mat.isValid() && mat.type() == type) + QmlObjectNode(mat).destroy(); }); }); + } else if (isItemBundle(bundleId)) { + // delete instances of the bundle item that is about to be unimported + executeInTransaction("ContentLibraryView::connectImporter", [&] { + NodeMetaInfo metaInfo = model()->metaInfo(type); + QList nodes = allModelNodesOfType(metaInfo); + for (ModelNode &node : nodes) + node.destroy(); + }); + } + }); +} - connect(userModel, &ContentLibraryUserModel::bundleMaterialUnimported, this, - &ContentLibraryView::updateBundleUserMaterialsImportedState); +bool ContentLibraryView::isMaterialBundle(const QString &bundleId) const +{ + auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils(); + return bundleId == compUtils.materialsBundleId() || bundleId == compUtils.userMaterialsBundleId(); +} + +// item bundle includes effects and 3D components +bool ContentLibraryView::isItemBundle(const QString &bundleId) const +{ + auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils(); + return bundleId == compUtils.effectsBundleId() || bundleId == compUtils.userEffectsBundleId() + || bundleId == compUtils.user3DBundleId(); } void ContentLibraryView::modelAttached(Model *model) @@ -290,7 +246,6 @@ void ContentLibraryView::modelAttached(Model *model) m_hasQuick3DImport = model->hasImport("QtQuick3D"); updateBundlesQuick3DVersion(); - updateBundleMaterialsImportedState(); const bool hasLibrary = Utils3D::materialLibraryNode(this).isValid(); m_widget->setHasMaterialLibrary(hasLibrary); @@ -302,8 +257,17 @@ void ContentLibraryView::modelAttached(Model *model) m_widget->setHasActive3DScene(m_sceneId != -1); m_widget->clearSearchFilter(); + // bundles loading has to happen here, otherwise project path is not ready which will + // cause bundle items types to resolve incorrectly + m_widget->materialsModel()->loadBundle(); m_widget->effectsModel()->loadBundle(); - updateBundleEffectsImportedState(); + m_widget->userModel()->loadBundles(); + + auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils(); + m_widget->updateImportedState(compUtils.materialsBundleId()); + m_widget->updateImportedState(compUtils.effectsBundleId()); + m_widget->updateImportedState(compUtils.userMaterialsBundleId()); + m_widget->updateImportedState(compUtils.user3DBundleId()); } void ContentLibraryView::modelAboutToBeDetached(Model *model) @@ -345,8 +309,7 @@ void ContentLibraryView::selectedNodesChanged(const QList &selectedNo return node.metaInfo().isQtQuick3DModel(); }); - m_widget->materialsModel()->setHasModelSelection(!m_selectedModels.isEmpty()); - m_widget->userModel()->setHasModelSelection(!m_selectedModels.isEmpty()); + m_widget->setHasModelSelection(!m_selectedModels.isEmpty()); } void ContentLibraryView::customNotification(const AbstractView *view, @@ -387,18 +350,29 @@ void ContentLibraryView::customNotification(const AbstractView *view, m_widget->addTexture(m_draggedBundleTexture); m_draggedBundleTexture = nullptr; - } else if (identifier == "drop_bundle_effect") { + } else if (identifier == "drop_bundle_item") { QTC_ASSERT(nodeList.size() == 1, return); - m_bundleEffectPos = data.size() == 1 ? data.first() : QVariant(); - m_widget->effectsModel()->addInstance(m_draggedBundleEffect); - m_bundleEffectTarget = nodeList.first() ? nodeList.first() : Utils3D::active3DSceneNode(this); + auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils(); + bool is3D = m_draggedBundleItem->type().startsWith(compUtils.user3DBundleType().toLatin1()); + + m_bundleItemPos = data.size() == 1 ? data.first() : QVariant(); + if (is3D) + m_widget->userModel()->add3DInstance(m_draggedBundleItem); + else + m_widget->effectsModel()->addInstance(m_draggedBundleItem); + m_bundleItemTarget = nodeList.first() ? nodeList.first() : Utils3D::active3DSceneNode(this); } else if (identifier == "add_material_to_content_lib") { QTC_ASSERT(nodeList.size() == 1 && data.size() == 1, return); addLibMaterial(nodeList.first(), data.first().value()); } else if (identifier == "add_assets_to_content_lib") { addLibAssets(data.first().toStringList()); + } else if (identifier == "add_3d_to_content_lib") { + if (nodeList.first().isComponent()) + addLib3DComponent(nodeList.first()); + else + addLib3DItem(nodeList.first()); } } @@ -528,58 +502,49 @@ void ContentLibraryView::applyBundleMaterialToDropTarget(const ModelNode &bundle #endif // Add a project material to Content Library's user tab -void ContentLibraryView::addLibMaterial(const ModelNode &mat, const QPixmap &icon) +void ContentLibraryView::addLibMaterial(const ModelNode &node, const QPixmap &iconPixmap) { auto bundlePath = Utils::FilePath::fromString(Paths::bundlesPathSetting() + "/User/materials/"); - auto [name, qml] = m_widget->userModel()->getUniqueLibMaterialNameAndQml( - mat.variantProperty("objectName").value().toString()); + QString name = node.variantProperty("objectName").value().toString(); + auto [qml, icon] = m_widget->userModel()->getUniqueLibMaterialNames(node.id()); - bundlePath.pathAppended("icons").createDir(); - bundlePath.pathAppended("images").createDir(); - bundlePath.pathAppended("shaders").createDir(); - - QString iconPath = QLatin1String("icons/%1.png").arg(mat.id()); + QString iconPath = QLatin1String("icons/%1").arg(icon); QString fullIconPath = bundlePath.pathAppended(iconPath).toString(); // save icon - bool iconSaved = icon.save(fullIconPath); + bool iconSaved = iconPixmap.save(fullIconPath); if (!iconSaved) qWarning() << __FUNCTION__ << "icon save failed"; // generate and save material Qml file - const QStringList depAssets = writeLibMaterialQml(mat, qml); + const QStringList depAssets = writeLibItemQml(node, qml); // add the material to the bundle json - QJsonObject &jsonRef = m_widget->userModel()->bundleJsonObjectRef(); - QJsonObject matsObj = jsonRef.value("materials").toObject(); - QJsonObject matObj; - matObj.insert("qml", qml); - matObj.insert("icon", iconPath); + QJsonObject &jsonRef = m_widget->userModel()->bundleJsonMaterialObjectRef(); + QJsonArray itemsArr = jsonRef.value("items").toArray(); + QJsonObject itemObj; + itemObj.insert("name", name); + itemObj.insert("qml", qml); + itemObj.insert("icon", iconPath); QJsonArray filesArr; for (const QString &assetPath : depAssets) filesArr.append(assetPath); - matObj.insert("files", filesArr); + itemObj.insert("files", filesArr); - matsObj.insert(name, matObj); - jsonRef.insert("materials", matsObj); - auto result = bundlePath.pathAppended("user_materials_bundle.json") + itemsArr.append(itemObj); + jsonRef["items"] = itemsArr; + + auto result = bundlePath.pathAppended(Constants::BUNDLE_JSON_FILENAME) .writeFileContents(QJsonDocument(jsonRef).toJson()); if (!result) qWarning() << __FUNCTION__ << result.error(); // copy material assets to bundle folder for (const QString &assetPath : depAssets) { - Asset asset(assetPath); - QString subDir; - if (asset.isImage()) - subDir = "images"; - else if (asset.isShader()) - subDir = "shaders"; - Utils::FilePath assetPathSource = DocumentManager::currentResourcePath().pathAppended(assetPath); - Utils::FilePath assetPathTarget = bundlePath.pathAppended(QString("%1/%2") - .arg(subDir, "/" + asset.fileName())); + Utils::FilePath assetPathTarget = bundlePath.pathAppended(assetPath); + assetPathTarget.parentDir().ensureWritableDir(); auto result = assetPathSource.copyFile(assetPathTarget); if (!result) @@ -589,14 +554,16 @@ void ContentLibraryView::addLibMaterial(const ModelNode &mat, const QPixmap &ico m_widget->userModel()->addMaterial(name, qml, QUrl::fromLocalFile(fullIconPath), depAssets); } -QStringList ContentLibraryView::writeLibMaterialQml(const ModelNode &mat, const QString &qml) +QStringList ContentLibraryView::writeLibItemQml(const ModelNode &node, const QString &qml) { QStringList depListIds; - auto [qmlString, assets] = modelNodeToQmlString(mat, depListIds); + auto [qmlString, assets] = modelNodeToQmlString(node, depListIds); qmlString.prepend("import QtQuick\nimport QtQuick3D\n\n"); - auto qmlPath = Utils::FilePath::fromString(Paths::bundlesPathSetting() + "/User/materials/" + qml); + QString itemType = QLatin1String(node.metaInfo().isQtQuick3DMaterial() ? "materials" : "3d"); + auto qmlPath = Utils::FilePath::fromString(QLatin1String("%1/User/%2/%3") + .arg(Paths::bundlesPathSetting(), itemType, qml)); auto result = qmlPath.writeFileContents(qmlString.toUtf8()); if (!result) qWarning() << __FUNCTION__ << result.error(); @@ -619,16 +586,24 @@ QPair> ContentLibraryView::modelNodeToQmlString(const Mod qml += indent + "id: " + (depth == 0 ? "root" : node.id()) + " \n\n"; + const QList excludedProps = {"x", "y", "z", "eulerRotation.x", "eulerRotation.y", + "eulerRotation.z", "scale.x", "scale.y", "scale.z", + "pivot.x", "pivot.y", "pivot.z"}; const QList matProps = node.properties(); for (const AbstractProperty &p : matProps) { + if (excludedProps.contains(p.name())) + continue; + if (p.isVariantProperty()) { QVariant pValue = p.toVariantProperty().value(); QString val; if (strcmp(pValue.typeName(), "QString") == 0 || strcmp(pValue.typeName(), "QColor") == 0) { val = QLatin1String("\"%1\"").arg(pValue.toString()); } else if (strcmp(pValue.typeName(), "QUrl") == 0) { - val = QLatin1String("\"%1\"").arg(pValue.toString()); - assets.insert(pValue.toString()); + QString pValueStr = pValue.toString(); + val = QLatin1String("\"%1\"").arg(pValueStr); + if (!pValueStr.startsWith("#")) + assets.insert(pValue.toString()); } else if (strcmp(pValue.typeName(), "QmlDesigner::Enumeration") == 0) { val = pValue.value().toString(); } else { @@ -662,19 +637,28 @@ void ContentLibraryView::addLibAssets(const QStringList &paths) auto bundlePath = Utils::FilePath::fromString(Paths::bundlesPathSetting() + "/User/textures"); QStringList pathsInBundle; + const QStringList existingTextures = Utils::transform(bundlePath.dirEntries(QDir::Files), + [](const Utils::FilePath &path) { + return path.fileName(); + }); + for (const QString &path : paths) { + auto assetFilePath = Utils::FilePath::fromString(path); + if (existingTextures.contains(assetFilePath.fileName())) + continue; + Asset asset(path); - auto assetPath = Utils::FilePath::fromString(path); // save icon - QString iconSavePath = bundlePath.pathAppended("icons/" + assetPath.baseName() + ".png").toString(); + QString iconSavePath = bundlePath.pathAppended("icons/" + assetFilePath.baseName() + ".png") + .toString(); QPixmap icon = asset.pixmap({120, 120}); bool iconSaved = icon.save(iconSavePath); if (!iconSaved) qWarning() << __FUNCTION__ << "icon save failed"; // save asset - auto result = assetPath.copyFile(bundlePath.pathAppended(asset.fileName())); + auto result = assetFilePath.copyFile(bundlePath.pathAppended(asset.fileName())); if (!result) qWarning() << __FUNCTION__ << result.error(); @@ -684,6 +668,158 @@ void ContentLibraryView::addLibAssets(const QStringList &paths) m_widget->userModel()->addTextures(pathsInBundle); } +void ContentLibraryView::addLib3DComponent(const ModelNode &node) +{ + auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils(); + + QString compBaseName = node.simplifiedTypeName(); + QString compFileName = compBaseName + ".qml"; + + Utils::FilePath compDir = DocumentManager::currentProjectDirPath() + .pathAppended(compUtils.import3dTypePath() + '/' + compBaseName); + + auto bundlePath = Utils::FilePath::fromString(Paths::bundlesPathSetting() + "/User/3d/"); + + // confirm overwrite if an item with same name exists + if (bundlePath.pathAppended(compFileName).exists()) { + // Show a QML confirmation dialog before proceeding + QMessageBox::StandardButton reply = QMessageBox::question(m_widget, tr("3D Item Exists"), + tr("A 3D item with the same name '%1' already exists in the Content Library, are you sure you want to overwrite it?") + .arg(compFileName), QMessageBox::Yes | QMessageBox::No); + if (reply == QMessageBox::No) + return; + + // before overwriting remove old item (to avoid partial items and dangling assets) + m_widget->userModel()->remove3DFromContentLibByName(compFileName); + } + + // generate and save icon + QString iconPath = QLatin1String("icons/%1").arg(UniqueName::generateId(compBaseName) + ".png"); + QString fullIconPath = bundlePath.pathAppended(iconPath).toString(); + genAndSaveIcon(compDir.pathAppended(compFileName).path(), fullIconPath); + + const Utils::FilePaths sourceFiles = compDir.dirEntries({{}, QDir::Files, QDirIterator::Subdirectories}); + const QStringList ignoreList {"_importdata.json", "qmldir", compBaseName + ".hints"}; + QStringList filesList; // 3D component's assets (dependencies) + + for (const Utils::FilePath &sourcePath : sourceFiles) { + Utils::FilePath relativePath = sourcePath.relativePathFrom(compDir); + if (ignoreList.contains(sourcePath.fileName()) || relativePath.startsWith("source scene")) + continue; + + Utils::FilePath targetPath = bundlePath.pathAppended(relativePath.path()); + targetPath.parentDir().ensureWritableDir(); + + // copy item from project to user bundle + auto result = sourcePath.copyFile(targetPath); + if (!result) + qWarning() << __FUNCTION__ << result.error(); + + if (sourcePath.fileName() != compFileName) // skip component file (only collect dependencies) + filesList.append(relativePath.path()); + } + + // add the item to the bundle json + QJsonObject &jsonRef = m_widget->userModel()->bundleJson3DObjectRef(); + QJsonArray itemsArr = jsonRef.value("items").toArray(); + itemsArr.append(QJsonObject { + {"name", node.simplifiedTypeName()}, + {"qml", compFileName}, + {"icon", iconPath}, + {"files", QJsonArray::fromStringList(filesList)} + }); + + jsonRef["items"] = itemsArr; + + auto result = bundlePath.pathAppended(Constants::BUNDLE_JSON_FILENAME) + .writeFileContents(QJsonDocument(jsonRef).toJson()); + if (!result) + qWarning() << __FUNCTION__ << result.error(); + + m_widget->userModel()->add3DItem(compBaseName, compFileName, QUrl::fromLocalFile(fullIconPath), + filesList); +} + +void ContentLibraryView::addLib3DItem(const ModelNode &node) +{ + auto bundlePath = Utils::FilePath::fromString(Paths::bundlesPathSetting() + "/User/3d/"); + + QString name = node.variantProperty("objectName").value().toString(); + auto [qml, icon] = m_widget->userModel()->getUniqueLib3DNames(node.id()); + QString iconPath = QLatin1String("icons/%1").arg(icon); + + if (name.isEmpty()) + name = node.id(); + + // generate and save item Qml file + const QStringList depAssets = writeLibItemQml(node, qml); + + // generate and save icon + QString qmlPath = QLatin1String("%1/User/3d/%2").arg(Paths::bundlesPathSetting(), qml); + QString fullIconPath = bundlePath.pathAppended(iconPath).toString(); + genAndSaveIcon(qmlPath, fullIconPath); + + // add the item to the bundle json + QJsonObject &jsonRef = m_widget->userModel()->bundleJson3DObjectRef(); + QJsonArray itemsArr = jsonRef.value("items").toArray(); + itemsArr.append(QJsonObject { + {"name", name}, + {"qml", qml}, + {"icon", iconPath}, + {"files", QJsonArray::fromStringList(depAssets)} + }); + + jsonRef["items"] = itemsArr; + + auto result = bundlePath.pathAppended(Constants::BUNDLE_JSON_FILENAME) + .writeFileContents(QJsonDocument(jsonRef).toJson()); + if (!result) + qWarning() << __FUNCTION__ << result.error(); + + // copy item's assets to bundle folder + for (const QString &assetPath : depAssets) { + Utils::FilePath assetPathSource = DocumentManager::currentResourcePath().pathAppended(assetPath); + Utils::FilePath assetPathTarget = bundlePath.pathAppended(assetPath); + assetPathTarget.parentDir().ensureWritableDir(); + + auto result = assetPathSource.copyFile(assetPathTarget); + if (!result) + qWarning() << __FUNCTION__ << result.error(); + } + + m_widget->userModel()->add3DItem(name, qml, QUrl::fromLocalFile(fullIconPath), depAssets); +} + +/** + * @brief Generates an icon image from a qml component + * @param qmlPath path to the qml component file to be rendered + * @param iconPath output save path of the generated icon + */ +void ContentLibraryView::genAndSaveIcon(const QString &qmlPath, const QString &iconPath) +{ + m_imageCache.requestSmallImage( + Utils::PathString{qmlPath}, + [&, qmlPath, iconPath](const QImage &image) { + bool iconSaved = image.save(iconPath); + if (iconSaved) + m_widget->userModel()->refresh3DSection(); + else + qWarning() << "ContentLibraryView::genAndSaveIcon(): icon save failed"; + }, + [&](ImageCache::AbortReason abortReason) { + if (abortReason == ImageCache::AbortReason::Abort) { + qWarning() << QLatin1String("ContentLibraryView::genAndSaveIcon(): icon generation " + "failed for path %1, reason: Abort").arg(qmlPath); + } else if (abortReason == ImageCache::AbortReason::Failed) { + qWarning() << QLatin1String("ContentLibraryView::genAndSaveIcon(): icon generation " + "failed for path %1, reason: Failed").arg(qmlPath); + } else if (abortReason == ImageCache::AbortReason::NoEntry) { + qWarning() << QLatin1String("ContentLibraryView::genAndSaveIcon(): icon generation " + "failed for path %1, reason: NoEntry").arg(qmlPath); + } + }); +} + ModelNode ContentLibraryView::getBundleMaterialDefaultInstance(const TypeName &type) { ModelNode matLib = Utils3D::materialLibraryNode(this); @@ -724,7 +860,7 @@ ModelNode ContentLibraryView::createMaterial(const TypeName &typeName) QString newName = QString::fromUtf8(typeName).replace(rgx, " \\1\\2").trimmed(); if (newName.endsWith(" Material")) newName.chop(9); // remove trailing " Material" - QString newId = model()->generateIdFromName(newName, "material"); + QString newId = model()->generateNewId(newName, "material"); newMatNode.setIdWithRefactoring(newId); VariantProperty objNameProp = newMatNode.variantProperty("objectName"); @@ -750,7 +886,7 @@ ModelNode ContentLibraryView::createMaterial(const NodeMetaInfo &metaInfo) QString newName = QString::fromLatin1(metaInfo.simplifiedTypeName()).replace(rgx, " \\1\\2").trimmed(); if (newName.endsWith(" Material")) newName.chop(9); // remove trailing " Material" - QString newId = model()->generateIdFromName(newName, "material"); + QString newId = model()->generateNewId(newName, "material"); newMatNode.setIdWithRefactoring(newId); VariantProperty objNameProp = newMatNode.variantProperty("objectName"); @@ -762,63 +898,6 @@ ModelNode ContentLibraryView::createMaterial(const NodeMetaInfo &metaInfo) } #endif -void ContentLibraryView::updateBundleMaterialsImportedState() -{ - using namespace Utils; - - if (!m_widget->materialsModel()->bundleImporter()) - return; - - QStringList importedBundleMats; - - FilePath materialBundlePath = m_widget->materialsModel()->bundleImporter()->resolveBundleImportPath(); - - if (materialBundlePath.exists()) { - importedBundleMats = transform(materialBundlePath.dirEntries({{"*.qml"}, QDir::Files}), - [](const FilePath &f) { return f.fileName().chopped(4); }); - } - - m_widget->materialsModel()->updateImportedState(importedBundleMats); -} - -void ContentLibraryView::updateBundleUserMaterialsImportedState() -{ - using namespace Utils; - - if (!m_widget->userModel()->bundleImporter()) - return; - - QStringList importedBundleMats; - - FilePath bundlePath = m_widget->userModel()->bundleImporter()->resolveBundleImportPath(); - - if (bundlePath.exists()) { - importedBundleMats = transform(bundlePath.dirEntries({{"*.qml"}, QDir::Files}), - [](const FilePath &f) { return f.fileName().chopped(4); }); - } - - m_widget->userModel()->updateImportedState(importedBundleMats); -} - -void ContentLibraryView::updateBundleEffectsImportedState() -{ - using namespace Utils; - - if (!m_widget->effectsModel()->bundleImporter()) - return; - - QStringList importedBundleEffs; - - FilePath bundlePath = m_widget->effectsModel()->bundleImporter()->resolveBundleImportPath(); - - if (bundlePath.exists()) { - importedBundleEffs = transform(bundlePath.dirEntries({{"*.qml"}, QDir::Files}), - [](const FilePath &f) { return f.fileName().chopped(4); }); - } - - m_widget->effectsModel()->updateImportedState(importedBundleEffs); -} - void ContentLibraryView::updateBundlesQuick3DVersion() { bool hasImport = false; @@ -853,6 +932,7 @@ void ContentLibraryView::updateBundlesQuick3DVersion() #endif m_widget->materialsModel()->setQuick3DImportVersion(major, minor); m_widget->effectsModel()->setQuick3DImportVersion(major, minor); + m_widget->userModel()->setQuick3DImportVersion(major, minor); } } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h index 03d42fa8bcd..914a8b8ea00 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h @@ -3,9 +3,10 @@ #pragma once -#include "abstractview.h" -#include "createtexture.h" -#include "nodemetainfo.h" +#include +#include +#include +#include #include #include @@ -14,7 +15,7 @@ QT_FORWARD_DECLARE_CLASS(QPixmap) namespace QmlDesigner { -class ContentLibraryEffect; +class ContentLibraryItem; class ContentLibraryMaterial; class ContentLibraryTexture; class ContentLibraryWidget; @@ -25,7 +26,8 @@ class ContentLibraryView : public AbstractView Q_OBJECT public: - ContentLibraryView(ExternalDependenciesInterface &externalDependencies); + ContentLibraryView(AsynchronousImageCache &imageCache, + ExternalDependenciesInterface &externalDependencies); ~ContentLibraryView() override; bool hasWidget() const override; @@ -48,15 +50,17 @@ public: const QVariant &data) override; private: - void connectUserBundle(); + void connectImporter(); + bool isMaterialBundle(const QString &bundleId) const; + bool isItemBundle(const QString &bundleId) const; void active3DSceneChanged(qint32 sceneId); - void updateBundleMaterialsImportedState(); - void updateBundleUserMaterialsImportedState(); - void updateBundleEffectsImportedState(); void updateBundlesQuick3DVersion(); - void addLibMaterial(const ModelNode &mat, const QPixmap &icon); + void addLibMaterial(const ModelNode &node, const QPixmap &iconPixmap); void addLibAssets(const QStringList &paths); - QStringList writeLibMaterialQml(const ModelNode &mat, const QString &qml); + void addLib3DComponent(const ModelNode &node); + void addLib3DItem(const ModelNode &node); + void genAndSaveIcon(const QString &qmlPath, const QString &iconPath); + QStringList writeLibItemQml(const ModelNode &node, const QString &qml); QPair> modelNodeToQmlString(const ModelNode &node, QStringList &depListIds, int depth = 0); @@ -74,12 +78,13 @@ private: #endif QPointer m_widget; QList m_bundleMaterialTargets; - ModelNode m_bundleEffectTarget; // target of the dropped bundle effect - QVariant m_bundleEffectPos; // pos of the dropped bundle effect + ModelNode m_bundleItemTarget; // target of the dropped bundle item + QVariant m_bundleItemPos; // pos of the dropped bundle item QList m_selectedModels; // selected 3D model nodes ContentLibraryMaterial *m_draggedBundleMaterial = nullptr; ContentLibraryTexture *m_draggedBundleTexture = nullptr; - ContentLibraryEffect *m_draggedBundleEffect = nullptr; + ContentLibraryItem *m_draggedBundleItem = nullptr; + AsynchronousImageCache &m_imageCache; bool m_bundleMaterialAddToSelected = false; bool m_hasQuick3DImport = false; qint32 m_sceneId = -1; diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp index 9375d43fd4f..72bece4c98b 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp @@ -3,8 +3,9 @@ #include "contentlibrarywidget.h" -#include "contentlibraryeffect.h" +#include "contentlibrarybundleimporter.h" #include "contentlibraryeffectsmodel.h" +#include "contentlibraryitem.h" #include "contentlibrarymaterial.h" #include "contentlibrarymaterialsmodel.h" #include "contentlibrarytexture.h" @@ -18,6 +19,7 @@ #include #include +#include #include #include @@ -40,7 +42,6 @@ #include #include #include -#include #include namespace QmlDesigner { @@ -67,18 +68,18 @@ bool ContentLibraryWidget::eventFilter(QObject *obj, QEvent *event) Model *model = document->currentModel(); QTC_ASSERT(model, return false); - if (m_effectToDrag) { + if (m_itemToDrag) { QMouseEvent *me = static_cast(event); if ((me->globalPos() - m_dragStartPoint).manhattanLength() > 20) { QByteArray data; QMimeData *mimeData = new QMimeData; QDataStream stream(&data, QIODevice::WriteOnly); - stream << m_effectToDrag->type(); - mimeData->setData(Constants::MIME_TYPE_BUNDLE_EFFECT, data); + stream << m_itemToDrag->type(); + mimeData->setData(Constants::MIME_TYPE_BUNDLE_ITEM, data); - emit bundleEffectDragStarted(m_effectToDrag); - model->startDrag(mimeData, m_effectToDrag->icon().toLocalFile()); - m_effectToDrag = nullptr; + emit bundleItemDragStarted(m_itemToDrag); + model->startDrag(mimeData, m_itemToDrag->icon().toLocalFile()); + m_itemToDrag = nullptr; } } else if (m_materialToDrag) { QMouseEvent *me = static_cast(event); @@ -112,7 +113,7 @@ bool ContentLibraryWidget::eventFilter(QObject *obj, QEvent *event) } } } else if (event->type() == QMouseEvent::MouseButtonRelease) { - m_effectToDrag = nullptr; + m_itemToDrag = nullptr; m_materialToDrag = nullptr; m_textureToDrag = nullptr; setIsDragging(false); @@ -122,7 +123,7 @@ bool ContentLibraryWidget::eventFilter(QObject *obj, QEvent *event) } ContentLibraryWidget::ContentLibraryWidget() - : m_quickWidget(new StudioQuickWidget(this)) + : m_quickWidget(Utils::makeUniqueObjectPtr(this)) , m_materialsModel(new ContentLibraryMaterialsModel(this)) , m_texturesModel(new ContentLibraryTexturesModel("Textures", this)) , m_environmentsModel(new ContentLibraryTexturesModel("Environments", this)) @@ -155,7 +156,7 @@ ContentLibraryWidget::ContentLibraryWidget() auto layout = new QVBoxLayout(this); layout->setContentsMargins({}); layout->setSpacing(0); - layout->addWidget(m_quickWidget.data()); + layout->addWidget(m_quickWidget.get()); updateSearch(); @@ -177,6 +178,67 @@ ContentLibraryWidget::ContentLibraryWidget() {"userModel", QVariant::fromValue(m_userModel.data())}}); reloadQmlSource(); + createImporter(); +} + +void ContentLibraryWidget::createImporter() +{ + m_importer = new ContentLibraryBundleImporter(); +#ifdef QDS_USE_PROJECTSTORAGE + connect(m_importer, + &ContentLibraryBundleImporter::importFinished, + this, + [&](const QmlDesigner::TypeName &typeName, const QString &bundleId) { + setImporterRunning(false); + if (typeName.size()) + updateImportedState(bundleId); + }); +#else + connect(m_importer, + &ContentLibraryBundleImporter::importFinished, + this, + [&](const QmlDesigner::NodeMetaInfo &metaInfo, const QString &bundleId) { + setImporterRunning(false); + if (metaInfo.isValid()) + updateImportedState(bundleId); + }); +#endif + + connect(m_importer, &ContentLibraryBundleImporter::unimportFinished, this, + [&](const QmlDesigner::NodeMetaInfo &metaInfo, const QString &bundleId) { + Q_UNUSED(metaInfo) + setImporterRunning(false); + updateImportedState(bundleId); + }); +} + +void ContentLibraryWidget::updateImportedState(const QString &bundleId) +{ + if (!m_importer) + return; + + Utils::FilePath bundlePath = m_importer->resolveBundleImportPath(bundleId); + + QStringList importedItems; + if (bundlePath.exists()) { + importedItems = transform(bundlePath.dirEntries({{"*.qml"}, QDir::Files}), + [](const Utils::FilePath &f) { return f.baseName(); }); + } + + auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils(); + if (bundleId == compUtils.materialsBundleId()) + m_materialsModel->updateImportedState(importedItems); + else if (bundleId == compUtils.effectsBundleId()) + m_effectsModel->updateImportedState(importedItems); + else if (bundleId == compUtils.userMaterialsBundleId()) + m_userModel->updateMaterialsImportedState(importedItems); + else if (bundleId == compUtils.user3DBundleId()) + m_userModel->update3DImportedState(importedItems); +} + +ContentLibraryBundleImporter *ContentLibraryWidget::importer() const +{ + return m_importer; } QVariantMap ContentLibraryWidget::readTextureBundleJson() @@ -578,12 +640,6 @@ void ContentLibraryWidget::markTextureUpdated(const QString &textureKey) m_environmentsModel->markTextureHasNoUpdates(subcategory, textureKey); } -bool ContentLibraryWidget::userBundleEnabled() const -{ - // TODO: this method is to be removed after user bundle implementation is complete - return Core::ICore::settings()->value("QML/Designer/UseExperimentalFeatures45", false).toBool(); -} - QSize ContentLibraryWidget::sizeHint() const { return {420, 420}; @@ -683,6 +739,20 @@ void ContentLibraryWidget::setIsQt6Project(bool b) emit isQt6ProjectChanged(); } +bool ContentLibraryWidget::importerRunning() const +{ + return m_importerRunning; +} + +void ContentLibraryWidget::setImporterRunning(bool b) +{ + if (m_importerRunning == b) + return; + + m_importerRunning = b; + emit importerRunningChanged(); +} + void ContentLibraryWidget::reloadQmlSource() { const QString materialBrowserQmlPath = qmlSourcesPath() + "/ContentLibrary.qml"; @@ -710,32 +780,9 @@ void ContentLibraryWidget::setIsDragging(bool val) } } -QString ContentLibraryWidget::findTextureBundlePath() +void ContentLibraryWidget::startDragItem(QmlDesigner::ContentLibraryItem *item, const QPointF &mousePos) { - QDir texBundleDir; - - if (!qEnvironmentVariable("TEXTURE_BUNDLE_PATH").isEmpty()) - texBundleDir.setPath(qEnvironmentVariable("TEXTURE_BUNDLE_PATH")); - else if (Utils::HostOsInfo::isMacHost()) - texBundleDir.setPath(QCoreApplication::applicationDirPath() + "/../Resources/texture_bundle"); - - // search for matBundleDir from exec dir and up - if (texBundleDir.dirName() == ".") { - texBundleDir.setPath(QCoreApplication::applicationDirPath()); - while (!texBundleDir.cd("texture_bundle") && texBundleDir.cdUp()) - ; // do nothing - - if (texBundleDir.dirName() != "texture_bundle") // bundlePathDir not found - return {}; - } - - return texBundleDir.path(); -} - -void ContentLibraryWidget::startDragEffect(QmlDesigner::ContentLibraryEffect *eff, - const QPointF &mousePos) -{ - m_effectToDrag = eff; + m_itemToDrag = item; m_dragStartPoint = mousePos.toPoint(); setIsDragging(true); } @@ -810,4 +857,18 @@ QPointer ContentLibraryWidget::userModel() const return m_userModel; } +bool ContentLibraryWidget::hasModelSelection() const +{ + return m_hasModelSelection; +} + +void ContentLibraryWidget::setHasModelSelection(bool b) +{ + if (b == m_hasModelSelection) + return; + + m_hasModelSelection = b; + emit hasModelSelectionChanged(); +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h index c4d51d0362b..8e96d9d2f3b 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h @@ -5,6 +5,10 @@ #include "createtexture.h" +#include + +#include + #include #include @@ -18,13 +22,15 @@ class StudioQuickWidget; namespace QmlDesigner { -class ContentLibraryEffect; +class ContentLibraryBundleImporter; class ContentLibraryEffectsModel; +class ContentLibraryItem; class ContentLibraryMaterial; class ContentLibraryMaterialsModel; class ContentLibraryTexture; class ContentLibraryTexturesModel; class ContentLibraryUserModel; +class NodeMetaInfo; class ContentLibraryWidget : public QFrame { @@ -34,6 +40,8 @@ class ContentLibraryWidget : public QFrame Q_PROPERTY(bool hasMaterialLibrary READ hasMaterialLibrary NOTIFY hasMaterialLibraryChanged) Q_PROPERTY(bool hasActive3DScene READ hasActive3DScene WRITE setHasActive3DScene NOTIFY hasActive3DSceneChanged) Q_PROPERTY(bool isQt6Project READ isQt6Project NOTIFY isQt6ProjectChanged) + Q_PROPERTY(bool importerRunning READ importerRunning WRITE setImporterRunning NOTIFY importerRunningChanged) + Q_PROPERTY(bool hasModelSelection READ hasModelSelection NOTIFY hasModelSelectionChanged) // Needed for a workaround for a bug where after drag-n-dropping an item, the ScrollView scrolls to a random position Q_PROPERTY(bool isDragging MEMBER m_isDragging NOTIFY isDraggingChanged) @@ -58,9 +66,14 @@ public: bool isQt6Project() const; void setIsQt6Project(bool b); - Q_INVOKABLE void handleSearchFilterChanged(const QString &filterText); + bool importerRunning() const; + void setImporterRunning(bool b); + + bool hasModelSelection() const; + void setHasModelSelection(bool b); void setMaterialsModel(QPointer newMaterialsModel); + void updateImportedState(const QString &bundleId); QPointer materialsModel() const; QPointer texturesModel() const; @@ -68,7 +81,8 @@ public: QPointer effectsModel() const; QPointer userModel() const; - Q_INVOKABLE void startDragEffect(QmlDesigner::ContentLibraryEffect *eff, const QPointF &mousePos); + Q_INVOKABLE void handleSearchFilterChanged(const QString &filterText); + Q_INVOKABLE void startDragItem(QmlDesigner::ContentLibraryItem *item, const QPointF &mousePos); Q_INVOKABLE void startDragMaterial(QmlDesigner::ContentLibraryMaterial *mat, const QPointF &mousePos); Q_INVOKABLE void startDragTexture(QmlDesigner::ContentLibraryTexture *tex, const QPointF &mousePos); Q_INVOKABLE void addImage(QmlDesigner::ContentLibraryTexture *tex); @@ -76,12 +90,13 @@ public: Q_INVOKABLE void addLightProbe(QmlDesigner::ContentLibraryTexture *tex); Q_INVOKABLE void updateSceneEnvState(); Q_INVOKABLE void markTextureUpdated(const QString &textureKey); - Q_INVOKABLE bool userBundleEnabled() const; QSize sizeHint() const override; + ContentLibraryBundleImporter *importer() const; + signals: - void bundleEffectDragStarted(QmlDesigner::ContentLibraryEffect *bundleEff); + void bundleItemDragStarted(QmlDesigner::ContentLibraryItem *item); void bundleMaterialDragStarted(QmlDesigner::ContentLibraryMaterial *bundleMat); void bundleTextureDragStarted(QmlDesigner::ContentLibraryTexture *bundleTex); void addTextureRequested(const QString texPath, QmlDesigner::AddTextureMode mode); @@ -91,6 +106,8 @@ signals: void hasActive3DSceneChanged(); void isDraggingChanged(); void isQt6ProjectChanged(); + void importerRunningChanged(); + void hasModelSelectionChanged(); protected: bool eventFilter(QObject *obj, QEvent *event) override; @@ -99,7 +116,6 @@ private: void reloadQmlSource(); void updateSearch(); void setIsDragging(bool val); - QString findTextureBundlePath(); void loadTextureBundles(); QVariantMap readTextureBundleJson(); bool fetchTextureBundleJson(const QDir &bundleDir); @@ -110,19 +126,21 @@ private: const QString &existingMetaFile, const QString downloadedMetaFile); QStringList saveNewTextures(const QDir &bundleDir, const QStringList &newFiles); void populateTextureBundleModels(); + void createImporter(); - QScopedPointer m_quickWidget; + Utils::UniqueObjectPtr m_quickWidget; QPointer m_materialsModel; QPointer m_texturesModel; QPointer m_environmentsModel; QPointer m_effectsModel; QPointer m_userModel; + ContentLibraryBundleImporter *m_importer = nullptr; QShortcut *m_qmlSourceUpdateShortcut = nullptr; QString m_filterText; - ContentLibraryEffect *m_effectToDrag = nullptr; + ContentLibraryItem *m_itemToDrag = nullptr; ContentLibraryMaterial *m_materialToDrag = nullptr; ContentLibraryTexture *m_textureToDrag = nullptr; QPoint m_dragStartPoint; @@ -132,6 +150,8 @@ private: bool m_hasQuick3DImport = false; bool m_isDragging = false; bool m_isQt6Project = false; + bool m_importerRunning = false; + bool m_hasModelSelection = false; QString m_textureBundleUrl; QString m_bundlePath; }; diff --git a/src/plugins/qmldesigner/components/curveeditor/detail/keyframeitem.cpp b/src/plugins/qmldesigner/components/curveeditor/detail/keyframeitem.cpp index a1c229f57e2..159e7c31ee1 100644 --- a/src/plugins/qmldesigner/components/curveeditor/detail/keyframeitem.cpp +++ b/src/plugins/qmldesigner/components/curveeditor/detail/keyframeitem.cpp @@ -422,7 +422,7 @@ QVariant KeyframeItem::itemChange(QGraphicsItem::GraphicsItemChange change, cons rseg.moveLeftTo(position); if (legalLeft() && legalRight()) { - if (qApp->keyboardModifiers().testFlag(Qt::ShiftModifier) && m_validPos.has_value()) { + if (qApp->keyboardModifiers().testFlag(Qt::ShiftModifier) && m_validPos) { if (m_firstPos) { auto firstToNow = QLineF(*m_firstPos, position); if (std::abs(firstToNow.dx()) > std::abs(firstToNow.dy())) diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.cpp index 2e8ef8304f6..63d5e958b19 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.cpp @@ -51,6 +51,8 @@ Edit3DCanvas::Edit3DCanvas(Edit3DWidget *parent) setAcceptDrops(true); setFocusPolicy(Qt::ClickFocus); m_busyIndicator->show(); + + installEventFilter(this); } void Edit3DCanvas::updateRenderImage(const QImage &img) @@ -79,11 +81,20 @@ QWidget *Edit3DCanvas::busyIndicator() const return m_busyIndicator; } +#ifdef Q_OS_MACOS +extern "C" bool AXIsProcessTrusted(); +#endif + void Edit3DCanvas::setFlyMode(bool enabled, const QPoint &pos) { if (m_flyMode == enabled) return; +#ifdef Q_OS_MACOS + if (!AXIsProcessTrusted()) + m_isTrusted = false; +#endif + m_flyMode = enabled; if (enabled) { @@ -132,6 +143,23 @@ void Edit3DCanvas::setFlyMode(bool enabled, const QPoint &pos) m_parent->view()->setFlyMode(enabled); } +bool Edit3DCanvas::eventFilter(QObject *obj, QEvent *event) +{ + if (m_flyMode && event->type() == QEvent::ShortcutOverride) { + // Suppress shortcuts that conflict with fly mode keys + 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, + Qt::Key_Alt, Qt::Key_Shift }; + auto ke = static_cast(event); + if (controlKeys.contains(ke->key())) + event->accept(); + } + + return QObject::eventFilter(obj, event); +} + void Edit3DCanvas::mousePressEvent(QMouseEvent *e) { m_contextMenuPending = false; @@ -171,7 +199,8 @@ void Edit3DCanvas::mouseMoveEvent(QMouseEvent *e) // 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(); + QPointF diff = m_isTrusted ? (m_hiddenCursorPos - e->globalPos()) : (m_lastCursorPos - e->globalPos()); + if (e->buttons() == (Qt::LeftButton | Qt::RightButton)) { m_parent->view()->emitView3DAction(View3DActionType::EditCameraMove, QVector3D{float(-diff.x()), float(-diff.y()), 0.f}); @@ -182,13 +211,26 @@ void Edit3DCanvas::mouseMoveEvent(QMouseEvent *e) // Skip first move to avoid undesirable jump occasionally when initiating flight mode m_flyModeFirstUpdate = false; } - QCursor::setPos(m_hiddenCursorPos); + + if (m_isTrusted) + QCursor::setPos(m_hiddenCursorPos); + else + m_lastCursorPos = e->globalPos(); } } void Edit3DCanvas::wheelEvent(QWheelEvent *e) { - m_parent->view()->sendInputEvent(e); + if (m_flyMode) { + // In fly mode, wheel controls the camera speed slider (value range 1-100) + double speed; + double mult; + m_parent->view()->getCameraSpeedAuxData(speed, mult); + speed = qMin(100., qMax(1., speed + double(e->angleDelta().y()) / 40.)); + m_parent->view()->setCameraSpeedAuxData(speed, mult); + } else { + m_parent->view()->sendInputEvent(e); + } QWidget::wheelEvent(e); } diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.h b/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.h index 39207554a73..16c1063dd61 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.h +++ b/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.h @@ -30,6 +30,7 @@ public: bool isFlyMode() const { return m_flyMode; } protected: + bool eventFilter(QObject *obj, QEvent *event) override; void mousePressEvent(QMouseEvent *e) override; void mouseReleaseEvent(QMouseEvent *e) override; void mouseDoubleClickEvent(QMouseEvent *e) override; @@ -52,10 +53,12 @@ private: qint32 m_activeScene = -1; QElapsedTimer m_usageTimer; qreal m_opacity = 1.0; + bool m_isTrusted = true; QWidget *m_busyIndicator = nullptr; bool m_flyMode = false; QPoint m_flyModeStartCursorPos; QPoint m_hiddenCursorPos; + QPoint m_lastCursorPos; qint64 m_flyModeStartTime = 0; bool m_flyModeFirstUpdate = false; bool m_contextMenuPending = false; diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp index 4712b048b14..bb7404f2522 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp @@ -20,9 +20,11 @@ #include "nodeinstanceview.h" #include "qmldesignerconstants.h" #include "qmldesignerplugin.h" +#include "qmlitemnode.h" #include "qmlvisualnode.h" #include "seekerslider.h" #include "snapconfiguration.h" +#include "variantproperty.h" #include #include @@ -133,6 +135,7 @@ void Edit3DView::updateActiveScene3D(const QVariantMap &sceneState) const QString orientationKey = QStringLiteral("globalOrientation"); const QString editLightKey = QStringLiteral("showEditLight"); const QString gridKey = QStringLiteral("showGrid"); + const QString showLookAtKey = QStringLiteral("showLookAt"); const QString selectionBoxKey = QStringLiteral("showSelectionBox"); const QString iconGizmoKey = QStringLiteral("showIconGizmo"); const QString cameraFrustumKey = QStringLiteral("showCameraFrustum"); @@ -187,6 +190,11 @@ void Edit3DView::updateActiveScene3D(const QVariantMap &sceneState) else m_showGridAction->action()->setChecked(false); + if (sceneState.contains(showLookAtKey)) + m_showLookAtAction->action()->setChecked(sceneState[showLookAtKey].toBool()); + else + m_showLookAtAction->action()->setChecked(false); + if (sceneState.contains(selectionBoxKey)) m_showSelectionBoxAction->action()->setChecked(sceneState[selectionBoxKey].toBool()); else @@ -235,36 +243,10 @@ void Edit3DView::updateActiveScene3D(const QVariantMap &sceneState) state.showWireframe = false; } - // Syncing background color only makes sense for children of View3D instances - bool syncValue = false; - bool syncEnabled = false; - bool desiredSyncValue = false; if (sceneState.contains(syncEnvBgKey)) - desiredSyncValue = sceneState[syncEnvBgKey].toBool(); - ModelNode checkNode = Utils3D::active3DSceneNode(this); - const bool activeSceneValid = checkNode.isValid(); - - while (checkNode.isValid()) { - if (checkNode.metaInfo().isQtQuick3DView3D()) { - syncValue = desiredSyncValue; - syncEnabled = true; - break; - } - if (checkNode.hasParentProperty()) - checkNode = checkNode.parentProperty().parentModelNode(); - else - break; - } - - if (activeSceneValid && syncValue != desiredSyncValue) { - // Update actual toolstate as well if we overrode it. - QTimer::singleShot(0, this, [this, syncValue]() { - emitView3DAction(View3DActionType::SyncEnvBackground, syncValue); - }); - } - - m_syncEnvBackgroundAction->action()->setChecked(syncValue); - m_syncEnvBackgroundAction->action()->setEnabled(syncEnabled); + m_syncEnvBackgroundAction->action()->setChecked(sceneState[syncEnvBgKey].toBool()); + else + m_syncEnvBackgroundAction->action()->setChecked(false); // Selection context change updates visible and enabled states SelectionContext selectionContext(this); @@ -273,6 +255,8 @@ void Edit3DView::updateActiveScene3D(const QVariantMap &sceneState) m_bakeLightsAction->currentContextChanged(selectionContext); syncCameraSpeedToNewView(); + + storeCurrentSceneEnvironment(); } void Edit3DView::modelAttached(Model *model) @@ -350,11 +334,10 @@ void Edit3DView::handleEntriesChanged() {EK_importedModels, {tr("Imported Models"), contextIcon(DesignerIcons::ImportedModelsIcon)}}}; #ifdef QDS_USE_PROJECTSTORAGE - const auto &projectStorage = *model()->projectStorage(); auto append = [&](const NodeMetaInfo &metaInfo, ItemLibraryEntryKeys key) { auto entries = metaInfo.itemLibrariesEntries(); if (entries.size()) - entriesMap[key].entryList.append(toItemLibraryEntries(entries, projectStorage)); + entriesMap[key].entryList.append(toItemLibraryEntries(entries)); }; append(model()->qtQuick3DModelMetaInfo(), EK_primitives); @@ -369,7 +352,7 @@ void Edit3DView::handleEntriesChanged() .generatedComponentUtils() .import3dTypePrefix(); - auto assetsModule = model()->module(import3dTypePrefix); + auto assetsModule = model()->module(import3dTypePrefix, Storage::ModuleKind::QmlLibrary); for (const auto &metaInfo : model()->metaInfosForModule(assetsModule)) append(metaInfo, EK_importedModels); @@ -386,9 +369,12 @@ void Edit3DView::handleEntriesChanged() } else if (entry.typeName() == "QtQuick3D.OrthographicCamera" || entry.typeName() == "QtQuick3D.PerspectiveCamera") { entryKey = EK_cameras; - } else if (entry.typeName().startsWith(QmlDesignerPlugin::instance()->documentManager() - .generatedComponentUtils().import3dTypePrefix().toUtf8()) - && NodeHints::fromItemLibraryEntry(entry).canBeDroppedInView3D()) { + } else if (entry.typeName().startsWith(QmlDesignerPlugin::instance() + ->documentManager() + .generatedComponentUtils() + .import3dTypePrefix() + .toUtf8()) + && NodeHints::fromItemLibraryEntry(entry, model()).canBeDroppedInView3D()) { entryKey = EK_importedModels; } else { continue; @@ -505,7 +491,7 @@ void Edit3DView::nodeAtPosReady(const ModelNode &modelNode, const QVector3D &pos } else if (m_nodeAtPosReqType == NodeAtPosReqType::BundleMaterialDrop) { emitCustomNotification("drop_bundle_material", {modelNode}); // To ContentLibraryView } else if (m_nodeAtPosReqType == NodeAtPosReqType::BundleEffectDrop) { - emitCustomNotification("drop_bundle_effect", {modelNode}, {pos3d}); // To ContentLibraryView + emitCustomNotification("drop_bundle_item", {modelNode}, {pos3d}); // To ContentLibraryView } else if (m_nodeAtPosReqType == NodeAtPosReqType::TextureDrop) { emitCustomNotification("apply_texture_to_model3D", {modelNode, m_droppedModelNode}); } else if (m_nodeAtPosReqType == NodeAtPosReqType::AssetDrop) { @@ -540,6 +526,21 @@ void Edit3DView::nodeRemoved(const ModelNode &, updateAlignActionStates(); } +void Edit3DView::propertiesRemoved(const QList &propertyList) +{ + maybeStoreCurrentSceneEnvironment(propertyList); +} + +void Edit3DView::bindingPropertiesChanged(const QList &propertyList, PropertyChangeFlags) +{ + maybeStoreCurrentSceneEnvironment(propertyList); +} + +void Edit3DView::variantPropertiesChanged(const QList &propertyList, PropertyChangeFlags) +{ + maybeStoreCurrentSceneEnvironment(propertyList); +} + void Edit3DView::sendInputEvent(QEvent *e) const { if (nodeInstanceView()) @@ -716,6 +717,30 @@ QPoint Edit3DView::resolveToolbarPopupPos(Edit3DAction *action) const return pos; } +template +void Edit3DView::maybeStoreCurrentSceneEnvironment(const QList &propertyList) +{ + QSet handledNodes; + QmlObjectNode sceneEnv; + for (const AbstractProperty &prop : propertyList) { + ModelNode node = prop.parentModelNode(); + const qint32 id = node.internalId(); + if (handledNodes.contains(id)) + continue; + + handledNodes.insert(id); + if (!node.metaInfo().isQtQuick3DSceneEnvironment()) + continue; + + if (!sceneEnv.isValid()) + sceneEnv = currentSceneEnv(); + if (sceneEnv == node) { + storeCurrentSceneEnvironment(); + break; + } + } +} + void Edit3DView::showContextMenu() { // If request for context menu is still pending, skip for now @@ -736,32 +761,6 @@ void Edit3DView::showContextMenu() 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() @@ -831,6 +830,75 @@ void Edit3DView::syncCameraSpeedToNewView() setCameraSpeedAuxData(speed, multiplier); } +QmlObjectNode Edit3DView::currentSceneEnv() +{ + PropertyName envProp{"environment"}; + ModelNode checkNode = Utils3D::active3DSceneNode(this); + while (checkNode.isValid()) { + if (checkNode.metaInfo().isQtQuick3DView3D()) { + QmlObjectNode sceneEnvNode = QmlItemNode(checkNode).bindingProperty(envProp) + .resolveToModelNode(); + if (sceneEnvNode.isValid()) + return sceneEnvNode; + break; + } + if (checkNode.hasParentProperty()) + checkNode = checkNode.parentProperty().parentModelNode(); + else + break; + } + return {}; +} + +void Edit3DView::storeCurrentSceneEnvironment() +{ + // If current active scene has scene environment, store relevant properties + QmlObjectNode sceneEnvNode = currentSceneEnv(); + if (sceneEnvNode.isValid()) { + QVariantMap lastSceneEnvData; + + auto insertPropValue = [](const PropertyName prop, const QmlObjectNode &node, + QVariantMap &map) { + if (!node.hasProperty(prop)) + return; + + map.insert(QString::fromUtf8(prop), node.modelValue(prop)); + }; + + auto insertTextureProps = [&](const PropertyName prop) { + // For now we just grab the absolute path of texture source for simplicity + if (!sceneEnvNode.hasProperty(prop)) + return; + + QmlObjectNode bindNode = QmlItemNode(sceneEnvNode).bindingProperty(prop) + .resolveToModelNode(); + if (bindNode.isValid()) { + QVariantMap props; + const PropertyName sourceProp = "source"; + if (bindNode.hasProperty(sourceProp)) { + Utils::FilePath qmlPath = Utils::FilePath::fromUrl( + model()->fileUrl()).absolutePath(); + Utils::FilePath sourcePath = Utils::FilePath::fromUrl( + bindNode.modelValue(sourceProp).toUrl()); + + sourcePath = qmlPath.resolvePath(sourcePath); + + props.insert(QString::fromUtf8(sourceProp), + sourcePath.absoluteFilePath().toUrl()); + } + lastSceneEnvData.insert(QString::fromUtf8(prop), props); + } + }; + + insertPropValue("backgroundMode", sceneEnvNode, lastSceneEnvData); + insertPropValue("clearColor", sceneEnvNode, lastSceneEnvData); + insertTextureProps("lightProbe"); + insertTextureProps("skyBoxCubeMap"); + + emitView3DAction(View3DActionType::SetLastSceneEnvData, lastSceneEnvData); + } +} + const QList &Edit3DView::splitToolStates() const { return m_splitToolStates; @@ -978,6 +1046,18 @@ void Edit3DView::createEdit3DActions() nullptr, QCoreApplication::translate("ShowGridAction", "Toggle the visibility of the helper grid.")); + m_showLookAtAction = std::make_unique( + QmlDesigner::Constants::EDIT3D_EDIT_SHOW_LOOKAT, + View3DActionType::ShowLookAt, + QCoreApplication::translate("ShowLookAtAction", "Show Look-at"), + QKeySequence(Qt::Key_L), + true, + true, + QIcon(), + this, + nullptr, + QCoreApplication::translate("ShowLookAtAction", "Toggle the visibility of the edit camera look-at indicator.")); + m_showSelectionBoxAction = std::make_unique( QmlDesigner::Constants::EDIT3D_EDIT_SHOW_SELECTION_BOX, View3DActionType::ShowSelectionBox, @@ -1281,6 +1361,7 @@ void Edit3DView::createEdit3DActions() m_rightActions << m_resetAction.get(); m_visibilityToggleActions << m_showGridAction.get(); + m_visibilityToggleActions << m_showLookAtAction.get(); m_visibilityToggleActions << m_showSelectionBoxAction.get(); m_visibilityToggleActions << m_showIconGizmoAction.get(); m_visibilityToggleActions << m_showCameraFrustumAction.get(); diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.h b/src/plugins/qmldesigner/components/edit3d/edit3dview.h index ade2ef6a8f9..755efc0ae38 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.h +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.h @@ -8,6 +8,7 @@ #include #include +#include #include #include @@ -59,6 +60,11 @@ public: PropertyChangeFlags propertyChange) override; void nodeRemoved(const ModelNode &removedNode, const NodeAbstractProperty &parentProperty, PropertyChangeFlags propertyChange) override; + void propertiesRemoved(const QList &propertyList) override; + void bindingPropertiesChanged(const QList &propertyList, + PropertyChangeFlags propertyChange) override; + void variantPropertiesChanged(const QList &propertyList, + PropertyChangeFlags propertyChange) override; void sendInputEvent(QEvent *e) const; void edit3DViewResized(const QSize &size) const; @@ -127,9 +133,14 @@ private: void createSyncEnvBackgroundAction(); void createSeekerSliderAction(); void syncCameraSpeedToNewView(); + QmlObjectNode currentSceneEnv(); + void storeCurrentSceneEnvironment(); QPoint resolveToolbarPopupPos(Edit3DAction *action) const; + template::value>::type> + void maybeStoreCurrentSceneEnvironment(const QList &propertyList); + QPointer m_edit3DWidget; QVector m_leftActions; QVector m_rightActions; @@ -148,6 +159,7 @@ private: std::unique_ptr m_orientationModeAction; std::unique_ptr m_editLightAction; std::unique_ptr m_showGridAction; + std::unique_ptr m_showLookAtAction; std::unique_ptr m_showSelectionBoxAction; std::unique_ptr m_showIconGizmoAction; std::unique_ptr m_showCameraFrustumAction; @@ -187,7 +199,6 @@ private: int m_activeSplit = 0; QList m_splitToolStates; - QList m_flyModeDisabledActions; ModelNode m_contextMenuPendingNode; ModelNode m_pickView3dNode; diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp index 6f1cf2e1837..02271518be9 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp @@ -2,37 +2,41 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "edit3dwidget.h" -#include "designdocument.h" -#include "designericons.h" + #include "edit3dactions.h" #include "edit3dcanvas.h" #include "edit3dtoolbarmenu.h" #include "edit3dview.h" -#include "externaldependenciesinterface.h" -#include "materialutils.h" -#include "metainfo.h" -#include "modelnodeoperations.h" -#include "nodeabstractproperty.h" -#include "nodehints.h" -#include "qmldesignerconstants.h" -#include "qmldesignerplugin.h" -#include "qmleditormenu.h" -#include "qmlvisualnode.h" -#include "viewmanager.h" -#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 @@ -359,6 +363,14 @@ void Edit3DWidget::createContextMenu() resetAction->setToolTip(tr("Reset all shading options for all viewports.")); m_contextMenu->addSeparator(); + + m_addToContentLibAction = m_contextMenu->addAction( + contextIcon(DesignerIcons::CreateIcon), // TODO: placeholder icon + tr("Add to Content Library"), [&] { + view()->emitCustomNotification("add_3d_to_content_lib", {m_contextMenuTarget}); + }); + + m_contextMenu->addSeparator(); } bool Edit3DWidget::isPasteAvailable() const @@ -608,14 +620,18 @@ void Edit3DWidget::showBackgroundColorMenu(bool show, const QPoint &pos) void Edit3DWidget::showContextMenu(const QPoint &pos, const ModelNode &modelNode, const QVector3D &pos3d) { + auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils(); + m_contextMenuTarget = modelNode; m_contextMenuPos3d = pos3d; const bool isModel = modelNode.metaInfo().isQtQuick3DModel(); + const bool isNode = modelNode.metaInfo().isQtQuick3DNode(); const bool allowAlign = view()->edit3DAction(View3DActionType::AlignCamerasToView)->action()->isEnabled(); const bool isSingleComponent = view()->hasSingleSelectedModelNode() && modelNode.isComponent(); const bool anyNodeSelected = view()->hasSelectedModelNodes(); const bool selectionExcludingRoot = anyNodeSelected && !view()->rootModelNode().isSelected(); + const bool isInBundle = modelNode.type().startsWith(compUtils.componentBundlesTypePrefix().toLatin1()); if (m_createSubMenu) m_createSubMenu->setEnabled(!isSceneLocked()); @@ -633,6 +649,7 @@ void Edit3DWidget::showContextMenu(const QPoint &pos, const ModelNode &modelNode m_toggleGroupAction->setEnabled(true); m_bakeLightsAction->setVisible(view()->bakeLightsAction()->action()->isVisible()); m_bakeLightsAction->setEnabled(view()->bakeLightsAction()->action()->isEnabled()); + m_addToContentLibAction->setEnabled(isNode && !isInBundle); if (m_view) { int idx = m_view->activeSplit(); @@ -685,7 +702,7 @@ void Edit3DWidget::dragEnterEvent(QDragEnterEvent *dragEnterEvent) } else if (actionManager.externalDragHasSupportedAssets(dragEnterEvent->mimeData()) || dragEnterEvent->mimeData()->hasFormat(Constants::MIME_TYPE_MATERIAL) || dragEnterEvent->mimeData()->hasFormat(Constants::MIME_TYPE_BUNDLE_MATERIAL) - || dragEnterEvent->mimeData()->hasFormat(Constants::MIME_TYPE_BUNDLE_EFFECT) + || dragEnterEvent->mimeData()->hasFormat(Constants::MIME_TYPE_BUNDLE_ITEM) || dragEnterEvent->mimeData()->hasFormat(Constants::MIME_TYPE_TEXTURE)) { if (Utils3D::active3DSceneNode(m_view).isValid()) dragEnterEvent->acceptProposedAction(); @@ -694,7 +711,7 @@ void Edit3DWidget::dragEnterEvent(QDragEnterEvent *dragEnterEvent) if (!data.isEmpty()) { QDataStream stream(data); stream >> m_draggedEntry; - if (NodeHints::fromItemLibraryEntry(m_draggedEntry).canBeDroppedInView3D()) + if (NodeHints::fromItemLibraryEntry(m_draggedEntry, view()->model()).canBeDroppedInView3D()) dragEnterEvent->acceptProposedAction(); } } @@ -730,8 +747,8 @@ void Edit3DWidget::dropEvent(QDropEvent *dropEvent) return; } - // handle dropping bundle effects - if (dropEvent->mimeData()->hasFormat(Constants::MIME_TYPE_BUNDLE_EFFECT)) { + // handle dropping bundle items + if (dropEvent->mimeData()->hasFormat(Constants::MIME_TYPE_BUNDLE_ITEM)) { m_view->dropBundleEffect(pos); m_view->model()->endDrag(); return; @@ -770,9 +787,10 @@ void Edit3DWidget::dropEvent(QDropEvent *dropEvent) ->documentManager() .generatedComponentUtils() .import3dTypePrefix(); - auto metaInfo = model->metaInfo(model->module(import3dTypePrefix), fileName.toUtf8()); + auto moduleId = model->module(import3dTypePrefix, Storage::ModuleKind::QmlLibrary); + auto metaInfo = model->metaInfo(moduleId, fileName.toUtf8()); if (auto entries = metaInfo.itemLibrariesEntries(); entries.size()) { - auto entry = ItemLibraryEntry{entries.front(), *model->projectStorage()}; + auto entry = ItemLibraryEntry{entries.front()}; QmlVisualNode::createQml3DNode(view(), entry, m_canvas->activeScene(), {}, false); } } diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h index 211b044d41d..97c0469668f 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h +++ b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h @@ -100,6 +100,7 @@ private: QPointer m_selectParentAction; QPointer m_toggleGroupAction; QPointer m_wireFrameAction; + QPointer m_addToContentLibAction; QHash> m_matOverrideActions; QPointer m_createSubMenu; ModelNode m_contextMenuTarget; diff --git a/src/plugins/qmldesigner/components/formeditor/dragtool.cpp b/src/plugins/qmldesigner/components/formeditor/dragtool.cpp index 0b7d199b500..a6494811b63 100644 --- a/src/plugins/qmldesigner/components/formeditor/dragtool.cpp +++ b/src/plugins/qmldesigner/components/formeditor/dragtool.cpp @@ -206,9 +206,16 @@ static ItemLibraryEntry itemLibraryEntryFromMimeData(const QMimeData *mimeData) return itemLibraryEntry; } -static bool canBeDropped(const QMimeData *mimeData) +static bool canBeDropped(const QMimeData *mimeData, Model *model) { - return NodeHints::fromItemLibraryEntry(itemLibraryEntryFromMimeData(mimeData)).canBeDroppedInFormEditor(); +#ifdef QDS_USE_PROJECTSTORAGE + auto itemLibraryEntry = itemLibraryEntryFromMimeData(mimeData); + NodeMetaInfo metaInfo{itemLibraryEntry.typeId(), model->projectStorage()}; + return metaInfo.canBeDroppedInFormEditor() == FlagIs::True; +#else + return NodeHints::fromItemLibraryEntry(itemLibraryEntryFromMimeData(mimeData), model) + .canBeDroppedInFormEditor(); +#endif } static bool hasItemLibraryInfo(const QMimeData *mimeData) @@ -218,7 +225,7 @@ static bool hasItemLibraryInfo(const QMimeData *mimeData) void DragTool::dropEvent(const QList &itemList, QGraphicsSceneDragDropEvent *event) { - if (canBeDropped(event->mimeData())) { + if (canBeDropped(event->mimeData(), view()->model())) { event->accept(); end(generateUseSnapping(event->modifiers())); @@ -290,7 +297,7 @@ void DragTool::dropEvent(const QList &itemList, QGraphicsSceneD void DragTool::dragEnterEvent(const QList &/*itemList*/, QGraphicsSceneDragDropEvent *event) { - if (canBeDropped(event->mimeData())) { + if (canBeDropped(event->mimeData(), view()->model())) { m_blockMove = false; if (hasItemLibraryInfo(event->mimeData())) { @@ -306,7 +313,7 @@ void DragTool::dragEnterEvent(const QList &/*itemList*/, QGraph void DragTool::dragLeaveEvent(const QList &/*itemList*/, QGraphicsSceneDragDropEvent *event) { - if (canBeDropped(event->mimeData())) { + if (canBeDropped(event->mimeData(), view()->model())) { event->accept(); m_moveManipulator.end(); @@ -363,10 +370,8 @@ void DragTool::dragMoveEvent(const QList &itemList, QGraphicsSc ->data(Constants::MIME_TYPE_ASSETS)).split(','); QString assetType = AssetsLibraryWidget::getAssetTypeAndData(assetPaths[0]).first; - if (!m_blockMove - && !m_isAborted - && canBeDropped(event->mimeData()) - && assetType != Constants::MIME_TYPE_ASSET_EFFECT) { + if (!m_blockMove && !m_isAborted && canBeDropped(event->mimeData(), view()->model()) + && assetType != Constants::MIME_TYPE_ASSET_EFFECT) { event->accept(); if (!m_dragNodes.isEmpty()) { if (targetContainerItem) { diff --git a/src/plugins/qmldesigner/components/formeditor/dragtool.h b/src/plugins/qmldesigner/components/formeditor/dragtool.h index 1cd2c9f4873..c1d5626d28d 100644 --- a/src/plugins/qmldesigner/components/formeditor/dragtool.h +++ b/src/plugins/qmldesigner/components/formeditor/dragtool.h @@ -7,7 +7,6 @@ #include "selectionindicator.h" #include -#include #include namespace QmlDesigner { diff --git a/src/plugins/qmldesigner/components/integration/designdocument.cpp b/src/plugins/qmldesigner/components/integration/designdocument.cpp index 804ac076e62..aa2dfd3b28a 100644 --- a/src/plugins/qmldesigner/components/integration/designdocument.cpp +++ b/src/plugins/qmldesigner/components/integration/designdocument.cpp @@ -150,7 +150,10 @@ bool DesignDocument::loadInFileComponent(const ModelNode &componentNode) if (!componentNode.isRootNode()) { //change to subcomponent model - changeToInFileComponentModel(createComponentTextModifier(m_documentTextModifier.data(), rewriterView(), componentText, componentNode)); + changeToInFileComponentModel(createComponentTextModifier(m_documentTextModifier.get(), + rewriterView(), + componentText, + componentNode)); } return true; @@ -281,11 +284,10 @@ void DesignDocument::moveNodesToPosition(const QList &nodes, const st parentProperty.reparentHere(pastedNode); QmlVisualNode visualNode(pastedNode); - if (!firstVisualNode.has_value() && visualNode.isValid()){ + if (!firstVisualNode && visualNode) { firstVisualNode = visualNode; - translationVect = (position.has_value() && firstVisualNode.has_value()) - ? position.value() - firstVisualNode->position() - : QVector3D(); + translationVect = (position && firstVisualNode) ? *position - firstVisualNode->position() + : QVector3D(); } visualNode.translate(translationVect); } @@ -377,9 +379,12 @@ void DesignDocument::loadDocument(QPlainTextEdit *edit) m_documentTextModifier.reset(new BaseTextEditModifier(qobject_cast(plainTextEdit()))); - connect(m_documentTextModifier.data(), &TextModifier::textChanged, this, &DesignDocument::updateQrcFiles); + connect(m_documentTextModifier.get(), + &TextModifier::textChanged, + this, + &DesignDocument::updateQrcFiles); - m_rewriterView->setTextModifier(m_documentTextModifier.data()); + m_rewriterView->setTextModifier(m_documentTextModifier.get()); m_inFileComponentTextModifier.reset(); @@ -399,7 +404,7 @@ void DesignDocument::changeToDocumentModel() if (edit) edit->document()->clearUndoRedoStacks(); - m_rewriterView->setTextModifier(m_documentTextModifier.data()); + m_rewriterView->setTextModifier(m_documentTextModifier.get()); m_inFileComponentModel.reset(); m_inFileComponentTextModifier.reset(); @@ -432,7 +437,7 @@ bool DesignDocument::hasProject() const void DesignDocument::setModified() { - if (!m_documentTextModifier.isNull()) + if (m_documentTextModifier) m_documentTextModifier->textDocument()->setModified(true); } @@ -448,7 +453,7 @@ void DesignDocument::changeToInFileComponentModel(ComponentTextModifier *textMod m_inFileComponentModel = createInFileComponentModel(); - m_rewriterView->setTextModifier(m_inFileComponentTextModifier.data()); + m_rewriterView->setTextModifier(m_inFileComponentTextModifier.get()); viewManager().attachRewriterView(); viewManager().attachViewsExceptRewriterAndComponetView(); @@ -675,7 +680,7 @@ void DesignDocument::selectAll() RewriterView *DesignDocument::rewriterView() const { - return m_rewriterView.data(); + return m_rewriterView.get(); } void DesignDocument::setEditor(Core::IEditor *editor) diff --git a/src/plugins/qmldesigner/components/integration/designdocument.h b/src/plugins/qmldesigner/components/integration/designdocument.h index 52089d67c1b..1f67ff4b30b 100644 --- a/src/plugins/qmldesigner/components/integration/designdocument.h +++ b/src/plugins/qmldesigner/components/integration/designdocument.h @@ -16,9 +16,10 @@ #include #include - #include +#include + QT_BEGIN_NAMESPACE class QPlainTextEdit; QT_END_NAMESPACE @@ -143,12 +144,12 @@ private: // variables ModelPointer m_documentModel; ModelPointer m_inFileComponentModel; QPointer m_textEditor; - QScopedPointer m_documentTextModifier; - QScopedPointer m_inFileComponentTextModifier; + std::unique_ptr m_documentTextModifier; + std::unique_ptr m_inFileComponentTextModifier; #ifndef QDS_USE_PROJECTSTORAGE - QScopedPointer m_subComponentManager; + std::unique_ptr m_subComponentManager; #endif - QScopedPointer m_rewriterView; + std::unique_ptr m_rewriterView; bool m_documentLoaded; ProjectExplorer::Target *m_currentTarget; ProjectStorageDependencies m_projectStorageDependencies; diff --git a/src/plugins/qmldesigner/components/integration/designdocumentview.cpp b/src/plugins/qmldesigner/components/integration/designdocumentview.cpp index 6ef95bf4c45..d97b9ff06f9 100644 --- a/src/plugins/qmldesigner/components/integration/designdocumentview.cpp +++ b/src/plugins/qmldesigner/components/integration/designdocumentview.cpp @@ -23,6 +23,8 @@ #include #include +#include + namespace QmlDesigner { DesignDocumentView::DesignDocumentView(ExternalDependenciesInterface &externalDependencies) @@ -116,14 +118,14 @@ QString DesignDocumentView::toText() const textEdit.setPlainText(imports + QStringLiteral("Item {\n}\n")); NotIndentingTextEditModifier modifier(&textEdit); - QScopedPointer rewriterView( - new RewriterView(externalDependencies(), RewriterView::Amend)); + std::unique_ptr rewriterView = std::make_unique(externalDependencies(), + RewriterView::Amend); rewriterView->setCheckSemanticErrors(false); rewriterView->setPossibleImportsEnabled(false); rewriterView->setTextModifier(&modifier); - outputModel->setRewriterView(rewriterView.data()); + outputModel->setRewriterView(rewriterView.get()); - ModelMerger merger(rewriterView.data()); + ModelMerger merger(rewriterView.get()); merger.replaceModel(rootModelNode()); diff --git a/src/plugins/qmldesigner/components/itemlibrary/import3dcanvas.cpp b/src/plugins/qmldesigner/components/itemlibrary/import3dcanvas.cpp new file mode 100644 index 00000000000..608bf42eb36 --- /dev/null +++ b/src/plugins/qmldesigner/components/itemlibrary/import3dcanvas.cpp @@ -0,0 +1,82 @@ +// 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 "import3dcanvas.h" + +#include +#include +#include +#include + +namespace QmlDesigner { + +static QImage createGradientImage(int width, int height) { + QImage image(width, height, QImage::Format_ARGB32_Premultiplied); + + QLinearGradient gradient(0, 0, 0, height); + gradient.setColorAt(0, QColor(0x999999)); + gradient.setColorAt(1, QColor(0x222222)); + + QPainter painter(&image); + painter.fillRect(0, 0, width, height, gradient); + + return image; +} + +Import3dCanvas::Import3dCanvas(QWidget *parent) + : QWidget(parent) +{ +} + +void Import3dCanvas::updateRenderImage(const QImage &img) +{ + m_image = img; + update(); +} + +void Import3dCanvas::paintEvent([[maybe_unused]] QPaintEvent *e) +{ + QWidget::paintEvent(e); + + QPainter painter(this); + + if (m_image.isNull()) { + QImage image = createGradientImage(width(), height()); + painter.drawImage(rect(), image, QRect(0, 0, image.width(), image.height())); + } else { + painter.drawImage(rect(), m_image, QRect(0, 0, m_image.width(), m_image.height())); + } +} + +void Import3dCanvas::resizeEvent(QResizeEvent *) +{ + emit requestImageUpdate(); +} + +void Import3dCanvas::mousePressEvent(QMouseEvent *e) +{ + if (e->buttons() == Qt::LeftButton) + m_dragPos = e->position(); + else + m_dragPos = {}; +} + +void Import3dCanvas::mouseReleaseEvent(QMouseEvent *) +{ + m_dragPos = {}; +} + +void Import3dCanvas::mouseMoveEvent(QMouseEvent *e) +{ + if (m_dragPos.isNull()) + return; + + const QPointF curPos = e->position(); + const QPointF delta = curPos - m_dragPos; + + m_dragPos = curPos; + + emit requestRotation(delta); +} + +} diff --git a/src/plugins/qmldesigner/components/itemlibrary/import3dcanvas.h b/src/plugins/qmldesigner/components/itemlibrary/import3dcanvas.h new file mode 100644 index 00000000000..72fb19acffc --- /dev/null +++ b/src/plugins/qmldesigner/components/itemlibrary/import3dcanvas.h @@ -0,0 +1,37 @@ +// 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 +#include +#include +#include + +namespace QmlDesigner { + +class Import3dCanvas : public QWidget +{ + Q_OBJECT + +public: + Import3dCanvas(QWidget *parent); + + void updateRenderImage(const QImage &img); + +signals: + void requestImageUpdate(); + void requestRotation(const QPointF &delta); + +protected: + void paintEvent(QPaintEvent *e) override; + void resizeEvent(QResizeEvent *e) override; + void mousePressEvent(QMouseEvent *e) override; + void mouseReleaseEvent(QMouseEvent *e) override; + void mouseMoveEvent(QMouseEvent *e) override; + +private: + QImage m_image; + QPointF m_dragPos; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/itemlibrary/import3dconnectionmanager.cpp b/src/plugins/qmldesigner/components/itemlibrary/import3dconnectionmanager.cpp new file mode 100644 index 00000000000..4c455e3c6d2 --- /dev/null +++ b/src/plugins/qmldesigner/components/itemlibrary/import3dconnectionmanager.cpp @@ -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 + +#include "import3dconnectionmanager.h" + +#include +#include + +#include + +namespace QmlDesigner { + +Import3dConnectionManager::Import3dConnectionManager() +{ + connections().clear(); // Remove default interactive puppets + connections().emplace_back("Import 3D", "import3dmode"); +} + +void Import3dConnectionManager::setPreviewImageCallback(ImageCallback callback) +{ + m_previewImageCallback = std::move(callback); +} + +void Import3dConnectionManager::dispatchCommand(const QVariant &command, + ConnectionManagerInterface::Connection &connection) +{ + static const int commandType = QMetaType::type("PuppetToCreatorCommand"); + + if (command.typeId() == commandType) { + auto cmd = command.value(); + switch (cmd.type()) { + case PuppetToCreatorCommand::Import3DPreviewImage: { + ImageContainer container = qvariant_cast(cmd.data()); + QImage image = container.image(); + if (!image.isNull()) + m_previewImageCallback(image); + break; + } + default: + break; + } + } else { + InteractiveConnectionManager::dispatchCommand(command, connection); + } +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/itemlibrary/import3dconnectionmanager.h b/src/plugins/qmldesigner/components/itemlibrary/import3dconnectionmanager.h new file mode 100644 index 00000000000..458d612ad2d --- /dev/null +++ b/src/plugins/qmldesigner/components/itemlibrary/import3dconnectionmanager.h @@ -0,0 +1,28 @@ +// 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 "interactiveconnectionmanager.h" + +QT_FORWARD_DECLARE_CLASS(QImage) + +namespace QmlDesigner { + +class Import3dConnectionManager : public InteractiveConnectionManager +{ +public: + using ImageCallback = std::function; + + Import3dConnectionManager(); + + void setPreviewImageCallback(ImageCallback callback); + +protected: + void dispatchCommand(const QVariant &command, Connection &connection) override; + +private: + ImageCallback m_previewImageCallback; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp index 2c690726029..1cbced4dfac 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp @@ -4,15 +4,19 @@ #include "itemlibraryassetimportdialog.h" #include "ui_itemlibraryassetimportdialog.h" +#include "import3dcanvas.h" +#include "import3dconnectionmanager.h" + #include #include +#include #include #include #include +#include #include #include -#include #include #include @@ -74,9 +78,10 @@ ItemLibraryAssetImportDialog::ItemLibraryAssetImportDialog( const QStringList &importFiles, const QString &defaulTargetDirectory, const QVariantMap &supportedExts, const QVariantMap &supportedOpts, const QJsonObject &defaultOpts, const QSet &preselectedFilesForOverwrite, - QWidget *parent) + AbstractView *view, QWidget *parent) : QDialog(parent) , ui(new Ui::ItemLibraryAssetImportDialog) + , m_view(view) , m_importer(this) , m_preselectedFilesForOverwrite(preselectedFilesForOverwrite) { @@ -107,11 +112,9 @@ ItemLibraryAssetImportDialog::ItemLibraryAssetImportDialog( if (m_quick3DFiles.size() != importFiles.size()) addWarning("Cannot import 3D and other assets simultaneously. Skipping non-3D assets."); - ui->buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Import")); - connect(ui->buttonBox->button(QDialogButtonBox::Ok), &QPushButton::clicked, - this, &ItemLibraryAssetImportDialog::onImport); + connect(ui->importButton, &QPushButton::clicked, this, &ItemLibraryAssetImportDialog::onImport); - ui->buttonBox->button(QDialogButtonBox::Ok)->setDefault(true); + ui->importButton->setDefault(true); ui->advancedSettingsButton->setStyleSheet( "QPushButton#advancedSettingsButton {background-color: transparent}"); @@ -210,10 +213,14 @@ ItemLibraryAssetImportDialog::ItemLibraryAssetImportDialog( ui->tabWidget->setCurrentIndex(0); } - connect(ui->buttonBox->button(QDialogButtonBox::Close), &QPushButton::clicked, + connect(ui->closeButton, &QPushButton::clicked, this, &ItemLibraryAssetImportDialog::onClose); connect(ui->tabWidget, &QTabWidget::currentChanged, this, &ItemLibraryAssetImportDialog::updateUi); + connect(canvas(), &Import3dCanvas::requestImageUpdate, + this, &ItemLibraryAssetImportDialog::onRequestImageUpdate); + connect(canvas(), &Import3dCanvas::requestRotation, + this, &ItemLibraryAssetImportDialog::onRequestRotation); connect(&m_importer, &ItemLibraryAssetImporter::errorReported, this, &ItemLibraryAssetImportDialog::addError); @@ -227,23 +234,35 @@ ItemLibraryAssetImportDialog::ItemLibraryAssetImportDialog( this, &ItemLibraryAssetImportDialog::onImportFinished); connect(&m_importer, &ItemLibraryAssetImporter::progressChanged, this, &ItemLibraryAssetImportDialog::setImportProgress); - - addInfo(tr("Select import options and press \"Import\" to import the following files:")); - for (const auto &file : std::as_const(m_quick3DFiles)) - addInfo(file); + connect(&m_importer, &ItemLibraryAssetImporter::importReadyForPreview, + this, &ItemLibraryAssetImportDialog::onImportReadyForPreview); connect(ui->advancedSettingsButton, &QPushButton::clicked, this, &ItemLibraryAssetImportDialog::toggleAdvanced); QTimer::singleShot(0, this, &ItemLibraryAssetImportDialog::updateUi); + + if (m_quick3DFiles.size() != 1) { + addInfo(tr("Select import options and press \"Import\" to import the following files:")); + } else { + addInfo(tr("Importing:")); + QTimer::singleShot(0, this, &ItemLibraryAssetImportDialog::onImport); + } + + for (const auto &file : std::as_const(m_quick3DFiles)) + addInfo(file); + + updateImportButtonState(); } ItemLibraryAssetImportDialog::~ItemLibraryAssetImportDialog() { + cleanupPreviewPuppet(); delete ui; } -void ItemLibraryAssetImportDialog::updateImport(const ModelNode &updateNode, +void ItemLibraryAssetImportDialog::updateImport(AbstractView *view, + const ModelNode &updateNode, const QVariantMap &supportedExts, const QVariantMap &supportedOpts) { @@ -332,7 +351,8 @@ void ItemLibraryAssetImportDialog::updateImport(const ModelNode &updateNode, {sourceInfo.absoluteFilePath()}, node.model()->fileUrl().toLocalFile(), supportedExts, supportedOpts, options, - preselectedFiles, Core::ICore::dialogParent()); + preselectedFiles, view, + Core::ICore::dialogParent()); importDlg->show(); } else { @@ -481,6 +501,7 @@ QGridLayout *ItemLibraryAssetImportDialog::createOptionsGrid( QJsonValue value(optCheck->isChecked()); optObj.insert("value", value); m_importOptions[optionsIndex].insert(optKey, optObj); + updateImportButtonState(); }); } else { // Simple options also exist in advanced, so don't connect simple controls directly @@ -488,13 +509,17 @@ QGridLayout *ItemLibraryAssetImportDialog::createOptionsGrid( auto *advCheck = qobject_cast( m_labelToControlWidgetMaps[optionsIndex].value(optKey)); if (advCheck) { - QObject::connect(optCheck, &QCheckBox::toggled, this, [optCheck, advCheck]() { - if (advCheck->isChecked() != optCheck->isChecked()) + QObject::connect(optCheck, &QCheckBox::toggled, this, [this, optCheck, advCheck]() { + if (advCheck->isChecked() != optCheck->isChecked()) { advCheck->setChecked(optCheck->isChecked()); + updateImportButtonState(); + } }); - QObject::connect(advCheck, &QCheckBox::toggled, this, [optCheck, advCheck]() { - if (advCheck->isChecked() != optCheck->isChecked()) + QObject::connect(advCheck, &QCheckBox::toggled, this, [this, optCheck, advCheck]() { + if (advCheck->isChecked() != optCheck->isChecked()) { optCheck->setChecked(advCheck->isChecked()); + updateImportButtonState(); + } }); } } @@ -530,6 +555,7 @@ QGridLayout *ItemLibraryAssetImportDialog::createOptionsGrid( QJsonValue value(optSpin->value()); optObj.insert("value", value); m_importOptions[optionsIndex].insert(optKey, optObj); + updateImportButtonState(); }); } else { auto *advSpin = qobject_cast( @@ -537,14 +563,18 @@ QGridLayout *ItemLibraryAssetImportDialog::createOptionsGrid( if (advSpin) { // Connect corresponding advanced control QObject::connect(optSpin, &QDoubleSpinBox::valueChanged, - this, [optSpin, advSpin] { - if (advSpin->value() != optSpin->value()) + this, [this, optSpin, advSpin] { + if (advSpin->value() != optSpin->value()) { advSpin->setValue(optSpin->value()); + updateImportButtonState(); + } }); QObject::connect(advSpin, &QDoubleSpinBox::valueChanged, - this, [optSpin, advSpin] { - if (advSpin->value() != optSpin->value()) + this, [this, optSpin, advSpin] { + if (advSpin->value() != optSpin->value()) { optSpin->setValue(advSpin->value()); + updateImportButtonState(); + } }); } } @@ -829,6 +859,145 @@ bool ItemLibraryAssetImportDialog::isHiddenOption(const QString &id) return hiddenOptions.contains(id); } +void ItemLibraryAssetImportDialog::startPreview() +{ + cleanupPreviewPuppet(); + + // Preview is done via custom QML file added into the temporary folder of the preview + QString previewQml = +R"( +import QtQuick +import QtQuick3D + +Rectangle { + id: root + width: %1 + height: %2 + + property alias sceneNode: sceneNode + property alias view3d: view3d + property string extents + property string sceneModelName: "%3" + + gradient: Gradient { + GradientStop { position: 1.0; color: "#222222" } + GradientStop { position: 0.0; color: "#999999" } + } + + View3D { + id: view3d + anchors.fill: parent + camera: viewCamera + + environment: SceneEnvironment { + antialiasingMode: SceneEnvironment.MSAA + antialiasingQuality: SceneEnvironment.VeryHigh + } + + PerspectiveCamera { + id: viewCamera + x: 600 + y: 600 + z: 600 + eulerRotation.x: -45 + eulerRotation.y: -45 + clipFar: 100000 + clipNear: 10 + } + + DirectionalLight { + rotation: viewCamera.rotation + } + + Node { + id: sceneNode + } + } + + Text { + anchors.bottom: parent.bottom + anchors.left: parent.left + color: "white" + text: root.extents + font.pixelSize: 14 + } +} +)"; + + QSize size = canvas()->size(); + previewQml = previewQml.arg(size.width()).arg(size.height()).arg(m_previewCompName); + + m_previewFile.writeFileContents(previewQml.toUtf8()); + + if (!m_previewFile.exists()) { + addWarning("Failed to write preview file."); + return; + } + + m_connectionManager = new Import3dConnectionManager; + 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_previewFile.toUrl()); +#endif + + auto textDocument = std::make_unique(previewQml); + auto modifier = std::make_unique(textDocument.get(), + QTextCursor{textDocument.get()}); + m_rewriterView->setTextModifier(modifier.get()); + m_model->setRewriterView(m_rewriterView); + + if (!m_rewriterView->errors().isEmpty()) { + addWarning("Preview scene creation failed."); + cleanupPreviewPuppet(); + return; + } + + m_nodeInstanceView->setTarget(m_view->nodeInstanceView()->target()); + + auto previewImageCallback = [this](const QImage &image) { + canvas()->updateRenderImage(image); + }; + + auto crashCallback = [&] { + addWarning("Preview process crashed."); + cleanupPreviewPuppet(); + }; + + m_connectionManager->setPreviewImageCallback(std::move(previewImageCallback)); + m_nodeInstanceView->setCrashCallback(std::move(crashCallback)); + + m_model->setNodeInstanceView(m_nodeInstanceView); +} + +void ItemLibraryAssetImportDialog::cleanupPreviewPuppet() +{ + if (m_model) { + m_model->setNodeInstanceView({}); + m_model->setRewriterView({}); + m_model.reset(); + } + + if (m_nodeInstanceView) + m_nodeInstanceView->setCrashCallback({}); + + if (m_connectionManager) + m_connectionManager->setPreviewImageCallback({}); + + delete m_rewriterView; + delete m_nodeInstanceView; + delete m_connectionManager; +} + +Import3dCanvas *ItemLibraryAssetImportDialog::canvas() +{ + return ui->import3dcanvas; +} + void ItemLibraryAssetImportDialog::resizeEvent(QResizeEvent *event) { m_dialogHeight = event->size().height(); @@ -837,8 +1006,13 @@ void ItemLibraryAssetImportDialog::resizeEvent(QResizeEvent *event) void ItemLibraryAssetImportDialog::setCloseButtonState(bool importing) { - ui->buttonBox->button(QDialogButtonBox::Close)->setEnabled(true); - ui->buttonBox->button(QDialogButtonBox::Close)->setText(importing ? tr("Cancel") : tr("Close")); + ui->closeButton->setEnabled(true); + ui->closeButton->setText(importing ? tr("Cancel") : tr("Close")); +} + +void ItemLibraryAssetImportDialog::updateImportButtonState() +{ + ui->importButton->setText(m_previewOptions == m_importOptions ? tr("Accept") : tr("Import")); } void ItemLibraryAssetImportDialog::addError(const QString &error, const QString &srcPath) @@ -860,14 +1034,25 @@ void ItemLibraryAssetImportDialog::addInfo(const QString &info, const QString &s void ItemLibraryAssetImportDialog::onImport() { - ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); + ui->importButton->setEnabled(false); + + if (!m_previewCompName.isEmpty() && m_previewOptions == m_importOptions) { + cleanupPreviewPuppet(); + m_importer.finalizeQuick3DImport(); + return; + } + setCloseButtonState(true); ui->progressBar->setValue(0); if (!m_quick3DFiles.isEmpty()) { - m_importer.importQuick3D(m_quick3DFiles, m_quick3DImportPath, - m_importOptions, m_extToImportOptionsMap, - m_preselectedFilesForOverwrite); + if (!m_previewCompName.isEmpty()) { + m_importer.reImportQuick3D(m_previewCompName, m_importOptions); + } else { + m_importer.importQuick3D(m_quick3DFiles, m_quick3DImportPath, + m_importOptions, m_extToImportOptionsMap, + m_preselectedFilesForOverwrite); + } } } @@ -881,10 +1066,37 @@ void ItemLibraryAssetImportDialog::setImportProgress(int value, const QString &t ui->progressBar->setValue(value); } +void ItemLibraryAssetImportDialog::onImportReadyForPreview(const QString &path, const QString &compName) +{ + addInfo(tr("Import is ready for preview.")); + if (m_previewCompName.isEmpty()) + addInfo(tr("Click \"Accept\" to finish the import or adjust options and click \"Import\" to import again.")); + + m_previewFile = Utils::FilePath::fromString(path).pathAppended(m_importer.previewFileName()); + m_previewCompName = compName; + m_previewOptions = m_importOptions; + QTimer::singleShot(0, this, &ItemLibraryAssetImportDialog::startPreview); + + ui->importButton->setEnabled(true); + updateImportButtonState(); +} + +void ItemLibraryAssetImportDialog::onRequestImageUpdate() +{ + if (m_nodeInstanceView) + m_nodeInstanceView->view3DAction(View3DActionType::Import3dUpdatePreviewImage, canvas()->size()); +} + +void ItemLibraryAssetImportDialog::onRequestRotation(const QPointF &delta) +{ + if (m_nodeInstanceView) + m_nodeInstanceView->view3DAction(View3DActionType::Import3dRotatePreviewModel, delta); +} + void ItemLibraryAssetImportDialog::onImportNearlyFinished() { // Canceling import is no longer doable - ui->buttonBox->button(QDialogButtonBox::Close)->setEnabled(false); + ui->closeButton->setEnabled(false); } void ItemLibraryAssetImportDialog::onImportFinished() @@ -894,18 +1106,27 @@ void ItemLibraryAssetImportDialog::onImportFinished() QString interruptStr = tr("Import interrupted."); addError(interruptStr); setImportProgress(0, interruptStr); + if (m_explicitClose) + QTimer::singleShot(1000, this, &ItemLibraryAssetImportDialog::doClose); } else { QString doneStr = tr("Import done."); addInfo(doneStr); setImportProgress(100, doneStr); if (m_closeOnFinish) { // Add small delay to allow user to visually confirm import finishing - QTimer::singleShot(1000, this, &ItemLibraryAssetImportDialog::onClose); + QTimer::singleShot(1000, this, &ItemLibraryAssetImportDialog::doClose); } } } void ItemLibraryAssetImportDialog::onClose() +{ + ui->importButton->setEnabled(false); + m_explicitClose = true; + doClose(); +} + +void ItemLibraryAssetImportDialog::doClose() { if (m_importer.isImporting()) { addInfo(tr("Canceling import.")); diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.h index c5da4782325..e7c49330561 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.h +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.h @@ -3,14 +3,19 @@ #pragma once #include "itemlibraryassetimporter.h" -#include "modelnode.h" + +#include + +#include #include #include +#include #include QT_BEGIN_NAMESPACE class QGridLayout; +class QPushButton; QT_END_NAMESPACE namespace Utils { @@ -19,6 +24,10 @@ class OutputFormatter; namespace QmlDesigner { class ItemLibraryAssetImporter; +class Import3dCanvas; +class Import3dConnectionManager; +class NodeInstanceView; +class RewriterView; namespace Ui { class ItemLibraryAssetImportDialog; @@ -35,10 +44,12 @@ public: const QVariantMap &supportedOpts, const QJsonObject &defaultOpts, const QSet &preselectedFilesForOverwrite, + AbstractView *view, QWidget *parent = nullptr); ~ItemLibraryAssetImportDialog(); - static void updateImport(const ModelNode &updateNode, + static void updateImport(AbstractView *view, + const ModelNode &updateNode, const QVariantMap &supportedExts, const QVariantMap &supportedOpts); @@ -52,12 +63,17 @@ private slots: private: void setCloseButtonState(bool importing); + void updateImportButtonState(); void onImport(); void setImportProgress(int value, const QString &text); + void onImportReadyForPreview(const QString &path, const QString &compName); + void onRequestImageUpdate(); + void onRequestRotation(const QPointF &delta); void onImportNearlyFinished(); void onImportFinished(); void onClose(); + void doClose(); void toggleAdvanced(); void createTab(const QString &tabLabel, int optionsIndex, const QJsonObject &groups); @@ -69,8 +85,19 @@ private: bool isSimpleOption(const QString &id); bool isHiddenOption(const QString &id); + void startPreview(); + void cleanupPreviewPuppet(); + Import3dCanvas *canvas(); + Ui::ItemLibraryAssetImportDialog *ui = nullptr; Utils::OutputFormatter *m_outputFormatter = nullptr; + QPointer m_connectionManager; + QPointer m_nodeInstanceView; + QPointer m_rewriterView; + QPointer m_view; + ModelPointer m_model; + Utils::FilePath m_previewFile; + QString m_previewCompName; struct OptionsData { @@ -83,6 +110,7 @@ private: QString m_quick3DImportPath; ItemLibraryAssetImporter m_importer; QVector m_importOptions; + QVector m_previewOptions; QHash m_extToImportOptionsMap; QSet m_preselectedFilesForOverwrite; bool m_closeOnFinish = true; @@ -91,5 +119,6 @@ private: OptionsData m_advancedData; bool m_advancedMode = false; int m_dialogHeight = 350; + bool m_explicitClose = false; }; } diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.ui b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.ui index 081bc36a3d3..e0b9d925fca 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.ui +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.ui @@ -6,15 +6,15 @@ 0 0 - 630 + 1100 350 Asset Import - - + + @@ -24,6 +24,12 @@ 2 + + + 550 + 0 + + 0 @@ -98,6 +104,12 @@ 0 + + + 0 + 0 + + @@ -111,16 +123,64 @@ - - - QDialogButtonBox::Close|QDialogButtonBox::Ok - - + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Close + + + + + + + Import + + + + + + + + + 1 + 0 + + + + + 300 + 300 + + + + + + + Import3dCanvas + QWidget +
    import3dcanvas.h
    + 1 +
    +
    diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp index ed1f8041e9c..010d00a970a 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp @@ -21,6 +21,7 @@ #include #include +#include #include #include @@ -58,8 +59,6 @@ void ItemLibraryAssetImporter::importQuick3D(const QStringList &inputFiles, const QHash &extToImportOptionsMap, const QSet &preselectedFilesForOverwrite) { - if (m_isImporting) - cancelImport(); reset(); m_isImporting = true; @@ -92,6 +91,53 @@ void ItemLibraryAssetImporter::importQuick3D(const QStringList &inputFiles, } } +void ItemLibraryAssetImporter::reImportQuick3D(const QString &assetName, + const QVector &options) +{ + if (!assetName.isEmpty() && !m_parseData.contains(assetName)) { + addError(tr("Attempted to reimport non-existing asset: %1").arg(assetName)); + return; + } + + ParseData &pd = m_parseData[assetName]; + // Change outDir just in case reimport generates different files + QDir oldDir = pd.outDir; + QString assetFolder = generateAssetFolderName(pd.assetName); + pd.outDir.cdUp(); + pd.outDir.mkpath(assetFolder); + + if (!pd.outDir.cd(assetFolder)) { + addError(tr("Could not access temporary asset directory: \"%1\".") + .arg(pd.outDir.filePath(assetFolder))); + return; + } + + if (oldDir.absolutePath().contains(tempDirNameBase())) + oldDir.removeRecursively(); + + m_isImporting = false; + m_cancelled = false; + + m_puppetProcess.reset(); + m_requiredImports.clear(); + m_currentImportId = 0; + m_puppetQueue.clear(); + + for (ParseData &pd : m_parseData) + pd.importId = -1; + + pd.options = options[pd.optionsIndex]; + pd.importId = 1; + + m_importFiles.remove(assetName); + + m_importIdToAssetNameMap.clear(); + m_importIdToAssetNameMap[pd.importId] = assetName; + + m_puppetQueue.append(pd.importId); + startNextImportProcess(); +} + bool ItemLibraryAssetImporter::isImporting() const { return m_isImporting; @@ -104,19 +150,19 @@ void ItemLibraryAssetImporter::cancelImport() notifyFinished(); } -void ItemLibraryAssetImporter::addError(const QString &errMsg, const QString &srcPath) const +void ItemLibraryAssetImporter::addError(const QString &errMsg, const QString &srcPath) { qCDebug(importerLog) << "Error: "<< errMsg << srcPath; emit errorReported(errMsg, srcPath); } -void ItemLibraryAssetImporter::addWarning(const QString &warningMsg, const QString &srcPath) const +void ItemLibraryAssetImporter::addWarning(const QString &warningMsg, const QString &srcPath) { qCDebug(importerLog) << "Warning: " << warningMsg << srcPath; emit warningReported(warningMsg, srcPath); } -void ItemLibraryAssetImporter::addInfo(const QString &infoMsg, const QString &srcPath) const +void ItemLibraryAssetImporter::addInfo(const QString &infoMsg, const QString &srcPath) { qCDebug(importerLog) << "Info: " << infoMsg << srcPath; emit infoReported(infoMsg, srcPath); @@ -127,8 +173,8 @@ void ItemLibraryAssetImporter::importProcessFinished([[maybe_unused]] int exitCo { m_puppetProcess.reset(); - if (m_parseData.contains(m_currentImportId)) { - const ParseData &pd = m_parseData[m_currentImportId]; + if (m_importIdToAssetNameMap.contains(m_currentImportId)) { + const ParseData &pd = m_parseData[m_importIdToAssetNameMap[m_currentImportId]]; QString errStr; if (exitStatus == QProcess::ExitStatus::CrashExit) { errStr = tr("Import process crashed."); @@ -151,11 +197,12 @@ void ItemLibraryAssetImporter::importProcessFinished([[maybe_unused]] int exitCo addError(tr("Asset import process failed: \"%1\".") .arg(pd.sourceInfo.absoluteFilePath())); addError(errStr); - m_parseData.remove(m_currentImportId); + m_parseData.remove(m_importIdToAssetNameMap[m_currentImportId]); + m_importIdToAssetNameMap.remove(m_currentImportId); } } - int finishedCount = m_parseData.size() - m_puppetQueue.size(); + int finishedCount = m_importIdToAssetNameMap.size() - m_puppetQueue.size(); if (!m_puppetQueue.isEmpty()) startNextImportProcess(); @@ -163,7 +210,7 @@ void ItemLibraryAssetImporter::importProcessFinished([[maybe_unused]] int exitCo notifyProgress(100); QTimer::singleShot(0, this, &ItemLibraryAssetImporter::postImport); } else { - notifyProgress(int(100. * (double(finishedCount) / double(m_parseData.size())))); + notifyProgress(int(100. * (double(finishedCount) / double(m_importIdToAssetNameMap.size())))); } } @@ -179,7 +226,7 @@ void ItemLibraryAssetImporter::reset() m_cancelled = false; delete m_tempDir; - m_tempDir = new QTemporaryDir; + m_tempDir = new QTemporaryDir(QDir::tempPath() + tempDirNameBase()); m_importFiles.clear(); m_overwrittenImports.clear(); m_puppetProcess.reset(); @@ -187,6 +234,7 @@ void ItemLibraryAssetImporter::reset() m_requiredImports.clear(); m_currentImportId = 0; m_puppetQueue.clear(); + m_importIdToAssetNameMap.clear(); } void ItemLibraryAssetImporter::parseFiles(const QStringList &filePaths, @@ -208,9 +256,11 @@ void ItemLibraryAssetImporter::parseFiles(const QStringList &filePaths, int index = extToImportOptionsMap.value(QFileInfo(file).suffix()); ParseData pd; pd.options = options[index]; + pd.optionsIndex = index; if (preParseQuick3DAsset(file, pd, preselectedFilesForOverwrite)) { pd.importId = ++m_importIdCounter; - m_parseData.insert(pd.importId, pd); + m_importIdToAssetNameMap[pd.importId] = pd.assetName; + m_parseData.insert(pd.assetName, pd); } notifyProgress(qRound(++count * quota), progressTitle); } @@ -239,7 +289,7 @@ bool ItemLibraryAssetImporter::preParseQuick3DAsset(const QString &file, ParseDa pd.targetDirPath = pd.targetDir.filePath(pd.assetName); - if (pd.outDir.exists(pd.assetName)) { + if (m_parseData.contains(pd.assetName)) { addWarning(tr("Skipped import of duplicate asset: \"%1\".").arg(pd.assetName)); return false; } @@ -288,11 +338,12 @@ bool ItemLibraryAssetImporter::preParseQuick3DAsset(const QString &file, ParseDa } } - pd.outDir.mkpath(pd.assetName); + QString assetFolder = generateAssetFolderName(pd.assetName); + pd.outDir.mkpath(assetFolder); - if (!pd.outDir.cd(pd.assetName)) { + if (!pd.outDir.cd(assetFolder)) { addError(tr("Could not access temporary asset directory: \"%1\".") - .arg(pd.outDir.filePath(pd.assetName))); + .arg(pd.outDir.filePath(assetFolder))); return false; } return true; @@ -425,7 +476,7 @@ void ItemLibraryAssetImporter::postParseQuick3DAsset(ParseData &pd) // Copy the original asset into a subdirectory assetFiles.insert(sourcePath, sourceSceneTargetFilePath(pd)); - m_importFiles.insert(assetFiles); + m_importFiles.insert(pd.assetName, assetFiles); } void ItemLibraryAssetImporter::copyImportedFiles() @@ -500,6 +551,12 @@ void ItemLibraryAssetImporter::keepUiAlive() const QApplication::processEvents(); } +QString ItemLibraryAssetImporter::generateAssetFolderName(const QString &assetName) const +{ + static int counter = 0; + return assetName + "_QDS_" + QString::number(counter++); +} + ItemLibraryAssetImporter::OverwriteResult ItemLibraryAssetImporter::confirmAssetOverwrite(const QString &assetName) { const QString title = tr("Overwrite Existing Asset?"); @@ -534,7 +591,8 @@ void ItemLibraryAssetImporter::startNextImportProcess() if (model && view) { bool done = false; while (!m_puppetQueue.isEmpty() && !done) { - const ParseData pd = m_parseData.value(m_puppetQueue.takeLast()); + const ParseData pd = m_parseData.value( + m_importIdToAssetNameMap.value(m_puppetQueue.takeLast())); QStringList puppetArgs; QJsonDocument optDoc(pd.options); @@ -557,7 +615,8 @@ void ItemLibraryAssetImporter::startNextImportProcess() } else { addError(tr("Failed to start import 3D asset process."), pd.sourceInfo.absoluteFilePath()); - m_parseData.remove(pd.importId); + const QString assetName = m_importIdToAssetNameMap.take(pd.importId); + m_parseData.remove(assetName); m_puppetProcess.reset(); } } @@ -573,8 +632,16 @@ void ItemLibraryAssetImporter::postImport() postParseQuick3DAsset(pd); } - if (!isCancelled()) - finalizeQuick3DImport(); + if (!isCancelled()) { + // TODO: Currently we only support import preview for single imports + if (m_parseData.size() != 1) { + finalizeQuick3DImport(); + } else { + const ParseData &pd = m_parseData[m_parseData.keys().first()]; + const QString importedComponentName = pd.assetName; + emit importReadyForPreview(pd.outDir.absolutePath(), importedComponentName); + } + } } void ItemLibraryAssetImporter::finalizeQuick3DImport() diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.h index 8ad7b5a2dec..abe96909514 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.h +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.h @@ -2,8 +2,6 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #pragma once -#include "import.h" - #include #include @@ -35,20 +33,28 @@ public: const QHash &extToImportOptionsMap, const QSet &preselectedFilesForOverwrite); + void reImportQuick3D(const QString &assetName, const QVector &options); + bool isImporting() const; void cancelImport(); bool isCancelled() const; - void addError(const QString &errMsg, const QString &srcPath = {}) const; - void addWarning(const QString &warningMsg, const QString &srcPath = {}) const; - void addInfo(const QString &infoMsg, const QString &srcPath = {}) const; + void addError(const QString &errMsg, const QString &srcPath = {}); + void addWarning(const QString &warningMsg, const QString &srcPath = {}); + void addInfo(const QString &infoMsg, const QString &srcPath = {}); + + QString previewFileName() const { return "QDSImport3dPreviewScene.qml"; } + QString tempDirNameBase() const { return "/qds3dimport"; } + + void finalizeQuick3DImport(); signals: - void errorReported(const QString &, const QString &) const; - void warningReported(const QString &, const QString &) const; - void infoReported(const QString &, const QString &) const; - void progressChanged(int value, const QString &text) const; - void importNearlyFinished() const; + void errorReported(const QString &, const QString &); + void warningReported(const QString &, const QString &); + void infoReported(const QString &, const QString &); + void progressChanged(int value, const QString &text); + void importReadyForPreview(const QString &path, const QString &compName); + void importNearlyFinished(); void importFinished(); private slots: @@ -63,7 +69,8 @@ private: QFileInfo sourceInfo; QString assetName; QString originalAssetName; - int importId; + int importId = -1; + int optionsIndex = -1; }; void notifyFinished(); @@ -79,6 +86,7 @@ private: void notifyProgress(int value, const QString &text); void notifyProgress(int value); void keepUiAlive() const; + QString generateAssetFolderName(const QString &assetName) const; enum class OverwriteResult { Skip, @@ -89,10 +97,9 @@ private: OverwriteResult confirmAssetOverwrite(const QString &assetName); void startNextImportProcess(); void postImport(); - void finalizeQuick3DImport(); QString sourceSceneTargetFilePath(const ParseData &pd); - QSet> m_importFiles; + QHash> m_importFiles; // Key: asset name QHash m_overwrittenImports; bool m_isImporting = false; bool m_cancelled = false; @@ -101,7 +108,8 @@ private: QProcessUniquePointer m_puppetProcess; int m_importIdCounter = 0; int m_currentImportId = 0; - QHash m_parseData; + QHash m_importIdToAssetNameMap; + QHash m_parseData; // Key: asset name QString m_progressTitle; QStringList m_requiredImports; QList m_puppetQueue; diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp index 3bff2105209..8ef9512e26a 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp @@ -313,51 +313,54 @@ void ItemLibraryModel::update(Model *model) beginResetModel(); clearSections(); - GeneratedComponentUtils compUtils = QmlDesignerPlugin::instance()->documentManager() - .generatedComponentUtils(); + const QString projectName = DocumentManager::currentProjectName(); + + auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils(); QStringList excludedImports { - compUtils.componentBundlesTypePrefix() + ".MaterialBundle", - compUtils.componentBundlesTypePrefix() + ".EffectBundle" + projectName, + compUtils.materialsBundleType(), + compUtils.effectsBundleType(), + compUtils.userMaterialsBundleType(), + compUtils.user3DBundleType(), + compUtils.userEffectsBundleType() }; // create import sections - const QString projectName = DocumentManager::currentProjectName(); const Imports usedImports = model->usedImports(); QHash importHash; for (const Import &import : model->imports()) { - if (import.url() != projectName) { - if (excludedImports.contains(import.url()) - || import.url().startsWith(compUtils.composedEffectsTypePrefix())) { - continue; - } - bool addNew = true; - bool isQuick3DAsset = import.url().startsWith(compUtils.import3dTypePrefix()); - QString importUrl = import.url(); - if (isQuick3DAsset) - importUrl = ItemLibraryImport::quick3DAssetsTitle(); - else if (import.isFileImport()) - importUrl = import.toString(true, true).remove("\""); + if (excludedImports.contains(import.url()) + || import.url().startsWith(compUtils.composedEffectsTypePrefix())) { + continue; + } - ItemLibraryImport *oldImport = importHash.value(importUrl); - if (oldImport && oldImport->sectionType() == ItemLibraryImport::SectionType::Quick3DAssets - && isQuick3DAsset) { - addNew = false; // add only 1 Quick3DAssets import section - } else if (oldImport && oldImport->importEntry().url() == import.url()) { - // Retain the higher version if multiples exist - if (oldImport->importEntry().toVersion() >= import.toVersion() || import.hasVersion()) - addNew = false; - else - delete oldImport; - } + bool addNew = true; + bool isQuick3DAsset = import.url().startsWith(compUtils.import3dTypePrefix()); + QString importUrl = import.url(); + if (isQuick3DAsset) + importUrl = ItemLibraryImport::quick3DAssetsTitle(); + else if (import.isFileImport()) + importUrl = import.toString(true, true).remove("\""); - if (addNew) { - auto sectionType = isQuick3DAsset ? ItemLibraryImport::SectionType::Quick3DAssets - : ItemLibraryImport::SectionType::Default; - ItemLibraryImport *itemLibImport = new ItemLibraryImport(import, this, sectionType); - itemLibImport->setImportUsed(usedImports.contains(import)); - importHash.insert(importUrl, itemLibImport); - } + ItemLibraryImport *oldImport = importHash.value(importUrl); + if (oldImport && oldImport->sectionType() == ItemLibraryImport::SectionType::Quick3DAssets + && isQuick3DAsset) { + addNew = false; // add only 1 Quick3DAssets import section + } else if (oldImport && oldImport->importEntry().url() == import.url()) { + // Retain the higher version if multiples exist + if (oldImport->importEntry().toVersion() >= import.toVersion() || import.hasVersion()) + addNew = false; + else + delete oldImport; + } + + if (addNew) { + auto sectionType = isQuick3DAsset ? ItemLibraryImport::SectionType::Quick3DAssets + : ItemLibraryImport::SectionType::Default; + ItemLibraryImport *itemLibImport = new ItemLibraryImport(import, this, sectionType); + itemLibImport->setImportUsed(usedImports.contains(import)); + importHash.insert(importUrl, itemLibImport); } } @@ -373,7 +376,7 @@ void ItemLibraryModel::update(Model *model) NodeMetaInfo metaInfo; if constexpr (useProjectStorage()) - metaInfo = entry.metaInfo(); + metaInfo = NodeMetaInfo{entry.typeId(), model->projectStorage()}; else metaInfo = model->metaInfo(entry.typeName()); @@ -385,7 +388,8 @@ void ItemLibraryModel::update(Model *model) || metaInfo.majorVersion() < 0); #endif bool isItem = valid && metaInfo.isQtQuickItem(); - bool forceVisibility = valid && NodeHints::fromItemLibraryEntry(entry).visibleInLibrary(); + bool forceVisibility = valid + && NodeHints::fromItemLibraryEntry(entry, model).visibleInLibrary(); if (m_flowMode) { isItem = metaInfo.isFlowViewItem(); diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp index feff523b9c6..e4f4ffcd9c6 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp @@ -164,7 +164,7 @@ void ItemLibraryView::updateImport3DSupport(const QVariantMap &supportMap) auto importDlg = new ItemLibraryAssetImportDialog(fileNames, defaultDir, m_importableExtensions3DMap, m_importOptions3DMap, {}, {}, - Core::ICore::dialogParent()); + this, Core::ICore::dialogParent()); int result = importDlg->exec(); return result == QDialog::Accepted ? AddFilesResult::succeeded() : AddFilesResult::cancelled(); @@ -198,7 +198,7 @@ void ItemLibraryView::customNotification(const AbstractView *view, const QString const QList &nodeList, const QList &data) { if (identifier == "UpdateImported3DAsset" && nodeList.size() > 0) { - ItemLibraryAssetImportDialog::updateImport(nodeList[0], + ItemLibraryAssetImportDialog::updateImport(this, nodeList[0], m_importableExtensions3DMap, m_importOptions3DMap); diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.cpp b/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.cpp index d36e78512bb..b74f3107412 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.cpp +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.cpp @@ -366,6 +366,9 @@ void MaterialBrowserModel::selectMaterial(int idx, bool force) if (idx != m_selectedIndex || force) { m_selectedIndex = idx; emit selectedIndexChanged(idx); + + m_selectedMaterialIsComponent = selectedMaterial().isComponent(); + emit selectedMaterialIsComponentChanged(); } } @@ -434,22 +437,20 @@ void MaterialBrowserModel::copyMaterialProperties(int idx, const QString §io QJsonObject propsSpecObj = m_propertyGroupsObj.value(m_copiedMaterialType).toObject(); if (propsSpecObj.contains(section)) { // should always be true const QJsonArray propNames = propsSpecObj.value(section).toArray(); - // auto == QJsonValueConstRef after 04dc959d49e5e3 / Qt 6.4, QJsonValueRef before - for (const auto &propName : propNames) + for (const QJsonValueConstRef &propName : propNames) copiedProps.append(propName.toString().toLatin1()); if (section == "Base") { // add QtQuick3D.Material base props as well QJsonObject propsMatObj = m_propertyGroupsObj.value("Material").toObject(); const QJsonArray propNames = propsMatObj.value("Base").toArray(); - // auto == QJsonValueConstRef after 04dc959d49e5e3 / Qt 6.4, QJsonValueRef before - for (const auto &propName : propNames) + for (const QJsonValueConstRef &propName : propNames) copiedProps.append(propName.toString().toLatin1()); } } } m_copiedMaterialProps.clear(); - for (const auto &propName : copiedProps) { + for (const PropertyName &propName : copiedProps) { PropertyCopyData data; data.name = propName; data.isValid = m_allPropsCopied || validProps.contains(propName); diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.h b/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.h index 337dce05507..3b6b64ec865 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.h +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.h @@ -3,7 +3,7 @@ #pragma once -#include "modelnode.h" +#include #include #include @@ -20,6 +20,7 @@ class MaterialBrowserModel : public QAbstractListModel Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged) Q_PROPERTY(int selectedIndex MEMBER m_selectedIndex NOTIFY selectedIndexChanged) + Q_PROPERTY(bool selectedMaterialIsComponent MEMBER m_selectedMaterialIsComponent NOTIFY selectedMaterialIsComponentChanged) Q_PROPERTY(bool hasQuick3DImport READ hasQuick3DImport WRITE setHasQuick3DImport NOTIFY hasQuick3DImportChanged) Q_PROPERTY(bool hasModelSelection READ hasModelSelection WRITE setHasModelSelection NOTIFY hasModelSelectionChanged) Q_PROPERTY(bool hasMaterialLibrary READ hasMaterialLibrary WRITE setHasMaterialLibrary NOTIFY hasMaterialLibraryChanged) @@ -110,6 +111,7 @@ signals: const QList &props, bool all); void isQt6ProjectChanged(); + void selectedMaterialIsComponentChanged(); private: bool isValidIndex(int idx) const; @@ -132,6 +134,7 @@ private: bool m_hasMaterialLibrary = false; bool m_allPropsCopied = true; bool m_isQt6Project = false; + bool m_selectedMaterialIsComponent = false; QString m_copiedMaterialType; QPointer m_view; diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp index 8bd57617285..7d90dffffc6 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp @@ -446,8 +446,13 @@ void QmlDesigner::MaterialBrowserView::loadPropertyGroups() if (!m_hasQuick3DImport || m_propertyGroupsLoaded || !model()) return; +#ifdef QDS_USE_PROJECTSTORAGE + // TODO + QString matPropsPath; +#else QString matPropsPath = model()->metaInfo("QtQuick3D.Material").importDirectoryPath() + "/designer/propertyGroups.json"; +#endif m_propertyGroupsLoaded = m_widget->materialBrowserModel()->loadPropertyGroups(matPropsPath); } diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.cpp b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.cpp index 7cf0a875bcf..8723611be07 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.cpp +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.cpp @@ -150,7 +150,7 @@ MaterialBrowserWidget::MaterialBrowserWidget(AsynchronousImageCache &imageCache, : m_materialBrowserView(view) , m_materialBrowserModel(new MaterialBrowserModel(view, this)) , m_materialBrowserTexturesModel(new MaterialBrowserTexturesModel(view, this)) - , m_quickWidget(new StudioQuickWidget(this)) + , m_quickWidget(Utils::makeUniqueObjectPtr(this)) , m_previewImageProvider(new PreviewImageProvider()) { QImage defaultImage; @@ -179,7 +179,7 @@ MaterialBrowserWidget::MaterialBrowserWidget(AsynchronousImageCache &imageCache, auto layout = new QVBoxLayout(this); layout->setContentsMargins({}); layout->setSpacing(0); - layout->addWidget(m_quickWidget.data()); + layout->addWidget(m_quickWidget.get()); updateSearch(); @@ -411,7 +411,7 @@ void MaterialBrowserWidget::setIsDragging(bool val) StudioQuickWidget *MaterialBrowserWidget::quickWidget() const { - return m_quickWidget.data(); + return m_quickWidget.get(); } void MaterialBrowserWidget::clearPreviewCache() @@ -434,10 +434,4 @@ QPointer MaterialBrowserWidget::materialBrowserTex return m_materialBrowserTexturesModel; } -bool MaterialBrowserWidget::userBundleEnabled() const -{ - // TODO: this method is to be removed after user bundle implementation is complete - return Core::ICore::settings()->value("QML/Designer/UseExperimentalFeatures45", false).toBool(); -} - } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.h b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.h index 97c994e5652..6506283f858 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.h +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.h @@ -7,6 +7,8 @@ #include +#include + #include QT_BEGIN_NAMESPACE @@ -61,7 +63,6 @@ public: Q_INVOKABLE void acceptTextureDropOnMaterial(int matIndex, const QString &texId); Q_INVOKABLE void focusMaterialSection(bool focusMatSec); Q_INVOKABLE void addMaterialToContentLibrary(); - Q_INVOKABLE bool userBundleEnabled() const; StudioQuickWidget *quickWidget() const; @@ -85,7 +86,7 @@ private: QPointer m_materialBrowserView; QPointer m_materialBrowserModel; QPointer m_materialBrowserTexturesModel; - QScopedPointer m_quickWidget; + Utils::UniqueObjectPtr m_quickWidget; QShortcut *m_qmlSourceUpdateShortcut = nullptr; PreviewImageProvider *m_previewImageProvider = nullptr; diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.cpp b/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.cpp index ecc460ae510..0e508f8f360 100644 --- a/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.cpp +++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.cpp @@ -80,8 +80,8 @@ public: MaterialEditorQmlBackend::MaterialEditorQmlBackend(MaterialEditorView *materialEditor) : m_quickWidget(Utils::makeUniqueObjectPtr()) - , m_materialEditorTransaction(new MaterialEditorTransaction(materialEditor)) - , m_contextObject(new MaterialEditorContextObject(m_quickWidget.get())) + , m_materialEditorTransaction(std::make_unique(materialEditor)) + , m_contextObject(std::make_unique(m_quickWidget.get())) , m_materialEditorImageProvider(new MaterialEditorImageProvider()) { m_quickWidget->setObjectName(Constants::OBJECT_NAME_MATERIAL_EDITOR); @@ -90,7 +90,7 @@ MaterialEditorQmlBackend::MaterialEditorQmlBackend(MaterialEditorView *materialE m_quickWidget->engine()->addImageProvider("materialEditor", m_materialEditorImageProvider); m_contextObject->setBackendValues(&m_backendValuesPropertyMap); m_contextObject->setModel(materialEditor->model()); - context()->setContextObject(m_contextObject.data()); + context()->setContextObject(m_contextObject.get()); QObject::connect(&m_backendValuesPropertyMap, &DesignerPropertyMap::valueChanged, materialEditor, &MaterialEditorView::changeValue); @@ -193,7 +193,7 @@ QQmlContext *MaterialEditorQmlBackend::context() const MaterialEditorContextObject *MaterialEditorQmlBackend::contextObject() const { - return m_contextObject.data(); + return m_contextObject.get(); } QQuickWidget *MaterialEditorQmlBackend::widget() const @@ -224,7 +224,7 @@ DesignerPropertyMap &MaterialEditorQmlBackend::backendValuesPropertyMap() MaterialEditorTransaction *MaterialEditorQmlBackend::materialEditorTransaction() const { - return m_materialEditorTransaction.data(); + return m_materialEditorTransaction.get(); } PropertyEditorValue *MaterialEditorQmlBackend::propertyValueForName(const QString &propertyName) @@ -267,12 +267,9 @@ void MaterialEditorQmlBackend::setup(const QmlObjectNode &selectedMaterialNode, // anchors m_backendAnchorBinding.setup(selectedMaterialNode.modelNode()); - context()->setContextProperties( - QVector{ - {{"anchorBackend"}, QVariant::fromValue(&m_backendAnchorBinding)}, - {{"transaction"}, QVariant::fromValue(m_materialEditorTransaction.data())} - } - ); + context()->setContextProperties(QVector{ + {{"anchorBackend"}, QVariant::fromValue(&m_backendAnchorBinding)}, + {{"transaction"}, QVariant::fromValue(m_materialEditorTransaction.get())}}); contextObject()->setSpecificsUrl(qmlSpecificsFile); contextObject()->setStateName(stateName); diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.h b/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.h index 0792a635ca3..9fd5fc23992 100644 --- a/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.h +++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.h @@ -11,6 +11,8 @@ #include +#include + class PropertyEditorValue; QT_BEGIN_NAMESPACE @@ -67,8 +69,8 @@ private: Utils::UniqueObjectPtr m_quickWidget = nullptr; QmlAnchorBindingProxy m_backendAnchorBinding; QmlModelNodeProxy m_backendModelNode; - QScopedPointer m_materialEditorTransaction; - QScopedPointer m_contextObject; + std::unique_ptr m_materialEditorTransaction; + std::unique_ptr m_contextObject; QPointer m_materialEditorImageProvider; }; diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp index e083310cdbb..21114267cbb 100644 --- a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp +++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp @@ -24,7 +24,6 @@ #include "qmldesignerplugin.h" #include "qmltimeline.h" #include "variantproperty.h" -#include #include #include @@ -63,10 +62,6 @@ MaterialEditorView::MaterialEditorView(ExternalDependenciesInterface &externalDe } }); - m_typeUpdateTimer.setSingleShot(true); - m_typeUpdateTimer.setInterval(500); - connect(&m_typeUpdateTimer, &QTimer::timeout, this, &MaterialEditorView::updatePossibleTypes); - QmlDesignerPlugin::trackWidgetFocusTime(m_stackedWidget, Constants::EVENT_MATERIALEDITOR_TIME); MaterialEditorDynamicPropertiesProxyModel::registerDeclarativeType(); @@ -333,8 +328,10 @@ void MaterialEditorView::resetView() setupQmlBackend(); - if (m_qmlBackEnd) + if (m_qmlBackEnd) { m_qmlBackEnd->emitSelectionChanged(); + updatePossibleTypes(); + } QTimer::singleShot(0, this, &MaterialEditorView::requestPreviewRender); @@ -605,7 +602,6 @@ void MaterialEditorView::setupQmlBackend() else m_dynamicPropertiesModel->reset(); - delayedTypeUpdate(); initPreviewData(); m_stackedWidget->setCurrentWidget(m_qmlBackEnd->widget()); @@ -690,21 +686,6 @@ void MaterialEditorView::initPreviewData() } } -void MaterialEditorView::delayedTypeUpdate() -{ - m_typeUpdateTimer.start(); -} - -[[maybe_unused]] static Import entryToImport(const ItemLibraryEntry &entry) -{ - if (entry.majorVersion() == -1 && entry.minorVersion() == -1) - return Import::createFileImport(entry.requiredImport()); - - return Import::createLibraryImport(entry.requiredImport(), - QString::number(entry.majorVersion()) + QLatin1Char('.') + - QString::number(entry.minorVersion())); -} - void MaterialEditorView::updatePossibleTypes() { QTC_ASSERT(model(), return); @@ -712,49 +693,21 @@ void MaterialEditorView::updatePossibleTypes() if (!m_qmlBackEnd) return; -#ifdef QDS_USE_PROJECTSTORAGE - auto heirs = model()->qtQuick3DMaterialMetaInfo().heirs(); - heirs.push_back(model()->qtQuick3DMaterialMetaInfo()); - auto entries = Utils::transform(heirs, [&](const auto &heir) { - return toItemLibraryEntries(heir.itemLibrariesEntries(), *model()->projectStorage()); - }); + static const QStringList basicTypes { + "CustomMaterial", + "DefaultMaterial", + "PrincipledMaterial", + "SpecularGlossyMaterial" + }; - // I am unsure about the code intention here -#else // Ensure basic types are always first - QStringList nonQuick3dTypes; - QStringList allTypes; + const QString matType = m_selectedMaterial.simplifiedTypeName(); - const QList itemLibEntries = m_itemLibraryInfo->entries(); - for (const ItemLibraryEntry &entry : itemLibEntries) { - NodeMetaInfo metaInfo = model()->metaInfo(entry.typeName()); - bool valid = metaInfo.isValid() - && (metaInfo.majorVersion() >= entry.majorVersion() - || metaInfo.majorVersion() < 0); - if (valid && metaInfo.isQtQuick3DMaterial()) { - bool addImport = entry.requiredImport().isEmpty(); - if (!addImport) { - Import import = entryToImport(entry); - addImport = model()->hasImport(import, true, true); - } - if (addImport) { - const QList typeSplit = entry.typeName().split('.'); - const QString typeName = QString::fromLatin1(typeSplit.last()); - if (typeSplit.size() == 2 && typeSplit.first() == "QtQuick3D") { - if (!allTypes.contains(typeName)) - allTypes.append(typeName); - } else if (!nonQuick3dTypes.contains(typeName)) { - nonQuick3dTypes.append(typeName); - } - } - } + if (basicTypes.contains(matType)) { + m_qmlBackEnd->contextObject()->setPossibleTypes(basicTypes); + return; } - allTypes.sort(); - nonQuick3dTypes.sort(); - allTypes.append(nonQuick3dTypes); - - m_qmlBackEnd->contextObject()->setPossibleTypes(allTypes); -#endif + m_qmlBackEnd->contextObject()->setPossibleTypes({matType}); } void MaterialEditorView::modelAttached(Model *model) @@ -774,20 +727,6 @@ void MaterialEditorView::modelAttached(Model *model) m_ensureMatLibTimer.start(500); } -#ifndef QDS_USE_PROJECTSTORAGE - if (m_itemLibraryInfo.data() != model->metaInfo().itemLibraryInfo()) { - if (m_itemLibraryInfo) { - disconnect(m_itemLibraryInfo.data(), &ItemLibraryInfo::entriesChanged, - this, &MaterialEditorView::delayedTypeUpdate); - } - m_itemLibraryInfo = model->metaInfo().itemLibraryInfo(); - if (m_itemLibraryInfo) { - connect(m_itemLibraryInfo.data(), &ItemLibraryInfo::entriesChanged, - this, &MaterialEditorView::delayedTypeUpdate); - } - } -#endif - if (!m_setupCompleted) { reloadQml(); m_setupCompleted = true; @@ -801,7 +740,8 @@ void MaterialEditorView::modelAboutToBeDetached(Model *model) { AbstractView::modelAboutToBeDetached(model); m_dynamicPropertiesModel->reset(); - m_qmlBackEnd->materialEditorTransaction()->end(); + if (auto transaction = m_qmlBackEnd->materialEditorTransaction()) + transaction->end(); m_qmlBackEnd->contextObject()->setHasMaterialLibrary(false); m_selectedMaterial = {}; } @@ -938,7 +878,8 @@ void MaterialEditorView::selectedNodesChanged(const QList &selectedNo m_selectedModels.append(node); } - m_qmlBackEnd->contextObject()->setHasModelSelection(!m_selectedModels.isEmpty()); + if (m_qmlBackEnd) + m_qmlBackEnd->contextObject()->setHasModelSelection(!m_selectedModels.isEmpty()); } void MaterialEditorView::currentStateChanged(const ModelNode &node) @@ -1020,7 +961,7 @@ void MaterialEditorView::renameMaterial(ModelNode &material, const QString &newN return; executeInTransaction(__FUNCTION__, [&] { - material.setIdWithRefactoring(model()->generateIdFromName(newName, "material")); + material.setIdWithRefactoring(model()->generateNewId(newName, "material")); VariantProperty objNameProp = material.variantProperty("objectName"); objNameProp.setValue(newName); @@ -1057,7 +998,7 @@ void MaterialEditorView::duplicateMaterial(const ModelNode &material) QString newName = sourceMat.modelNode().variantProperty("objectName").value().toString() + " copy"; VariantProperty objNameProp = duplicateMatNode.variantProperty("objectName"); objNameProp.setValue(newName); - duplicateMatNode.setIdWithoutRefactoring(model()->generateIdFromName(newName, "material")); + duplicateMatNode.setIdWithoutRefactoring(model()->generateNewId(newName, "material")); // sync properties. Only the base state is duplicated. const QList props = material.properties(); diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.h b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.h index c201742bd51..11bea460638 100644 --- a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.h +++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.h @@ -109,12 +109,10 @@ private: bool noValidSelection() const; void initPreviewData(); - void delayedTypeUpdate(); void updatePossibleTypes(); ModelNode m_selectedMaterial; QTimer m_ensureMatLibTimer; - QTimer m_typeUpdateTimer; QShortcut *m_updateShortcut = nullptr; int m_timerId = 0; QStackedWidget *m_stackedWidget = nullptr; diff --git a/src/plugins/qmldesigner/components/navigator/choosefrompropertylistdialog.cpp b/src/plugins/qmldesigner/components/navigator/choosefrompropertylistdialog.cpp index a3ab5f2cd71..fee3218af00 100644 --- a/src/plugins/qmldesigner/components/navigator/choosefrompropertylistdialog.cpp +++ b/src/plugins/qmldesigner/components/navigator/choosefrompropertylistdialog.cpp @@ -102,10 +102,9 @@ ChooseFromPropertyListFilter::ChooseFromPropertyListFilter(const NodeMetaInfo &i #ifdef QDS_USE_PROJECTSTORAGE // TODO add the types here or use the module #else - } else if (insertInfo.typeName().startsWith( - QString("%1.MaterialBundle").arg(QmlDesignerPlugin::instance()->documentManager() - .generatedComponentUtils().componentBundlesTypePrefix()) - .toUtf8())) { + } else if (insertInfo.typeName().startsWith( + QmlDesignerPlugin::instance()->documentManager() + .generatedComponentUtils().materialsBundleType().toUtf8())) { if (parentInfo.isQtQuick3DModel()) propertyList.append("materials"); #endif diff --git a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp index d305753ead3..c5fa30fc7d8 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp +++ b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp @@ -455,7 +455,7 @@ QStringList NavigatorTreeModel::mimeTypes() const Constants::MIME_TYPE_MATERIAL, Constants::MIME_TYPE_BUNDLE_TEXTURE, Constants::MIME_TYPE_BUNDLE_MATERIAL, - Constants::MIME_TYPE_BUNDLE_EFFECT, + Constants::MIME_TYPE_BUNDLE_ITEM, Constants::MIME_TYPE_ASSETS}); return types; @@ -570,9 +570,9 @@ bool NavigatorTreeModel::dropMimeData(const QMimeData *mimeData, } else if (mimeData->hasFormat(Constants::MIME_TYPE_BUNDLE_MATERIAL)) { if (targetNode.isValid()) m_view->emitCustomNotification("drop_bundle_material", {targetNode}); // To ContentLibraryView - } else if (mimeData->hasFormat(Constants::MIME_TYPE_BUNDLE_EFFECT)) { + } else if (mimeData->hasFormat(Constants::MIME_TYPE_BUNDLE_ITEM)) { if (targetNode.isValid()) - m_view->emitCustomNotification("drop_bundle_effect", {targetNode}); // To ContentLibraryView + m_view->emitCustomNotification("drop_bundle_item", {targetNode}); // To ContentLibraryView } else if (mimeData->hasFormat(Constants::MIME_TYPE_ASSETS)) { const QStringList assetsPaths = QString::fromUtf8(mimeData->data(Constants::MIME_TYPE_ASSETS)).split(','); NodeAbstractProperty targetProperty; @@ -705,7 +705,7 @@ void NavigatorTreeModel::handleItemLibraryItemDrop(const QMimeData *mimeData, in const ItemLibraryEntry itemLibraryEntry = createItemLibraryEntryFromMimeData(mimeData->data(Constants::MIME_TYPE_ITEM_LIBRARY_INFO)); - const NodeHints hints = NodeHints::fromItemLibraryEntry(itemLibraryEntry); + const NodeHints hints = NodeHints::fromItemLibraryEntry(itemLibraryEntry, m_view->model()); const QString targetPropertyName = hints.forceNonDefaultProperty(); diff --git a/src/plugins/qmldesigner/components/previewtooltip/previewtooltipbackend.cpp b/src/plugins/qmldesigner/components/previewtooltip/previewtooltipbackend.cpp index 16328ae532f..b6d5e2ae476 100644 --- a/src/plugins/qmldesigner/components/previewtooltip/previewtooltipbackend.cpp +++ b/src/plugins/qmldesigner/components/previewtooltip/previewtooltipbackend.cpp @@ -42,7 +42,18 @@ void PreviewTooltipBackend::showTooltip() } }); }, - [](auto) {}, + [&](ImageCache::AbortReason abortReason) { + if (abortReason == ImageCache::AbortReason::Abort) { + qWarning() << QLatin1String("PreviewTooltipBackend::showTooltip(): preview generation " + "failed for path %1, reason: Abort").arg(m_path); + } else if (abortReason == ImageCache::AbortReason::Failed) { + qWarning() << QLatin1String("PreviewTooltipBackend::showTooltip(): preview generation " + "failed for path %1, reason: Failed").arg(m_path); + } else if (abortReason == ImageCache::AbortReason::NoEntry) { + qWarning() << QLatin1String("PreviewTooltipBackend::showTooltip(): preview generation " + "failed for path %1, reason: NoEntry").arg(m_path); + } + }, Utils::PathString{m_extraId}, m_auxiliaryData); diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp index eea53c3f5a1..c397d445b19 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp @@ -83,16 +83,17 @@ namespace QmlDesigner { PropertyEditorQmlBackend::PropertyEditorQmlBackend(PropertyEditorView *propertyEditor, AsynchronousImageCache &imageCache) : m_view(Utils::makeUniqueObjectPtr(imageCache)) - , m_propertyEditorTransaction(new PropertyEditorTransaction(propertyEditor)) - , m_dummyPropertyEditorValue(new PropertyEditorValue()) - , m_contextObject(new PropertyEditorContextObject(m_view.get())) + , m_propertyEditorTransaction(std::make_unique(propertyEditor)) + , m_dummyPropertyEditorValue(std::make_unique()) + , m_contextObject(std::make_unique(m_view.get())) { m_view->engine()->setOutputWarningsToStandardError(QmlDesignerPlugin::instance() ->settings().value(DesignerSettingsKey::SHOW_PROPERTYEDITOR_WARNINGS).toBool()); m_view->engine()->addImportPath(propertyEditorResourcesPath() + "/imports"); m_dummyPropertyEditorValue->setValue(QLatin1String("#000000")); - context()->setContextProperty(QLatin1String("dummyBackendValue"), m_dummyPropertyEditorValue.data()); + context()->setContextProperty(QLatin1String("dummyBackendValue"), + m_dummyPropertyEditorValue.get()); m_contextObject->setBackendValues(&m_backendValuesPropertyMap); m_contextObject->setModel(propertyEditor->model()); m_contextObject->insertInQmlContext(context()); @@ -402,7 +403,7 @@ QQmlContext *PropertyEditorQmlBackend::context() PropertyEditorContextObject *PropertyEditorQmlBackend::contextObject() { - return m_contextObject.data(); + return m_contextObject.get(); } QQuickWidget *PropertyEditorQmlBackend::widget() @@ -432,7 +433,7 @@ DesignerPropertyMap &PropertyEditorQmlBackend::backendValuesPropertyMap() { } PropertyEditorTransaction *PropertyEditorQmlBackend::propertyEditorTransaction() { - return m_propertyEditorTransaction.data(); + return m_propertyEditorTransaction.get(); } PropertyEditorValue *PropertyEditorQmlBackend::propertyValueForName(const QString &propertyName) @@ -495,12 +496,9 @@ void PropertyEditorQmlBackend::setup(const QmlObjectNode &qmlObjectNode, const Q // anchors m_backendAnchorBinding.setup(qmlObjectNode.modelNode()); - context()->setContextProperties( - QVector{ - {{"anchorBackend"}, QVariant::fromValue(&m_backendAnchorBinding)}, - {{"transaction"}, QVariant::fromValue(m_propertyEditorTransaction.data())} - } - ); + context()->setContextProperties(QVector{ + {{"anchorBackend"}, QVariant::fromValue(&m_backendAnchorBinding)}, + {{"transaction"}, QVariant::fromValue(m_propertyEditorTransaction.get())}}); contextObject()->setHasMultiSelection( !qmlObjectNode.view()->singleSelectedModelNode().isValid()); @@ -592,13 +590,10 @@ void PropertyEditorQmlBackend::initialSetup(const TypeName &typeName, const QUrl QObject::connect(valueObject, &PropertyEditorValue::valueChanged, &backendValuesPropertyMap(), &DesignerPropertyMap::valueChanged); m_backendValuesPropertyMap.insert(QLatin1String("id"), QVariant::fromValue(valueObject)); - context()->setContextProperties( - QVector{ - {{"anchorBackend"}, QVariant::fromValue(&m_backendAnchorBinding)}, - {{"modelNodeBackend"}, QVariant::fromValue(&m_backendModelNode)}, - {{"transaction"}, QVariant::fromValue(m_propertyEditorTransaction.data())} - } - ); + context()->setContextProperties(QVector{ + {{"anchorBackend"}, QVariant::fromValue(&m_backendAnchorBinding)}, + {{"modelNodeBackend"}, QVariant::fromValue(&m_backendModelNode)}, + {{"transaction"}, QVariant::fromValue(m_propertyEditorTransaction.get())}}); contextObject()->setSpecificsUrl(qmlSpecificsFile); diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h index 120b7b4abcf..1c9b808ac3d 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h @@ -16,6 +16,8 @@ #include +#include + class PropertyEditorValue; namespace QmlDesigner { @@ -109,9 +111,9 @@ private: Utils::UniqueObjectPtr m_view = nullptr; QmlAnchorBindingProxy m_backendAnchorBinding; QmlModelNodeProxy m_backendModelNode; - QScopedPointer m_propertyEditorTransaction; - QScopedPointer m_dummyPropertyEditorValue; - QScopedPointer m_contextObject; + std::unique_ptr m_propertyEditorTransaction; + std::unique_ptr m_dummyPropertyEditorValue; + std::unique_ptr m_contextObject; }; } //QmlDesigner diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp index f731d6803b1..27319c15f56 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp @@ -22,7 +22,6 @@ #include #include -#include #include namespace QmlDesigner { @@ -549,6 +548,23 @@ void PropertyEditorValue::setForceBound(bool b) emit isBoundChanged(); } +void PropertyEditorValue::insertKeyframe() +{ + if (!m_modelNode.isValid()) + return; + + /*If we add more code here we have to forward the property editor view */ + AbstractView *view = m_modelNode.view(); + + QmlTimeline timeline = view->currentTimeline(); + + QTC_ASSERT(timeline.isValid(), return ); + QTC_ASSERT(m_modelNode.isValid(), return ); + + view->executeInTransaction("PropertyEditorContextObject::insertKeyframe", + [&] { timeline.insertKeyframe(m_modelNode, name()); }); +} + QStringList PropertyEditorValue::generateStringList(const QString &string) const { QString copy = string; diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h index 70a51fffc2c..c4b09f6b5af 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h @@ -172,6 +172,8 @@ public: Q_INVOKABLE void setForceBound(bool b); + Q_INVOKABLE void insertKeyframe(); + public slots: void resetValue(); void setEnumeration(const QString &scope, const QString &name); diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp index b22b39e238b..e0d5759617b 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp @@ -38,7 +38,6 @@ #include #include #include -#include #include #include diff --git a/src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.cpp b/src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.cpp index fc39b371375..1cff0e55e68 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.cpp @@ -161,13 +161,12 @@ void QmlModelNodeProxy::createModelNode(int internalIdParent, const QString &typeName, const QString &requiredImport) { - QTC_ASSERT(m_qmlObjectNode.isValid(), return ); - - auto modelNode = m_qmlObjectNode.modelNode(); - - AbstractView *view = modelNode.view(); - auto parentModelNode = m_qmlObjectNode.modelNode(); + + QTC_ASSERT(parentModelNode.isValid(), return ); + + AbstractView *view = parentModelNode.view(); + if (internalIdParent >= 0) parentModelNode = view->modelNodeForInternalId(internalIdParent); @@ -186,7 +185,7 @@ void QmlModelNodeProxy::createModelNode(int internalIdParent, #ifdef QDS_USE_PROJECTSTORAGE ModelNode newNode = view->createModelNode(typeName.toUtf8()); #else - NodeMetaInfo metaInfo = modelNode.model()->metaInfo(typeName.toUtf8()); + NodeMetaInfo metaInfo = parentModelNode.model()->metaInfo(typeName.toUtf8()); ModelNode newNode = view->createModelNode(metaInfo.typeName(), metaInfo.majorVersion(), metaInfo.minorVersion()); @@ -200,14 +199,17 @@ void QmlModelNodeProxy::moveNode(int internalIdParent, int fromIndex, int toIndex) { - QTC_ASSERT(m_qmlObjectNode.isValid(), return ); + ModelNode modelNode = m_qmlObjectNode.modelNode(); - ModelNode node = m_qmlObjectNode.view()->modelNodeForInternalId(internalIdParent); + QTC_ASSERT(modelNode.isValid(), return ); - QTC_ASSERT(node.isValid(), return ); + if (internalIdParent >= 0) + modelNode = m_qmlObjectNode.view()->modelNodeForInternalId(internalIdParent); + + QTC_ASSERT(modelNode.isValid(), return ); AbstractView *view = m_qmlObjectNode.view(); - view->executeInTransaction("QmlModelNodeProxy::swapNode", [&] { - node.nodeListProperty(propertyName.toUtf8()).slide(fromIndex, toIndex); + view->executeInTransaction("QmlModelNodeProxy::moveNode", [&] { + modelNode.nodeListProperty(propertyName.toUtf8()).slide(fromIndex, toIndex); }); } diff --git a/src/plugins/qmldesigner/components/texteditor/texteditorview.cpp b/src/plugins/qmldesigner/components/texteditor/texteditorview.cpp index d400251648e..2e52b3358ba 100644 --- a/src/plugins/qmldesigner/components/texteditor/texteditorview.cpp +++ b/src/plugins/qmldesigner/components/texteditor/texteditorview.cpp @@ -96,7 +96,8 @@ void TextEditorView::modelAboutToBeDetached(Model *model) { AbstractView::modelAboutToBeDetached(model); - m_widget->setTextEditor(nullptr); + if (m_widget) + m_widget->setTextEditor(nullptr); // in case the user closed it explicit we do not want to do anything with the editor if (Core::ModeManager::currentModeId() == Core::Constants::MODE_DESIGN) { diff --git a/src/plugins/qmldesigner/components/texttool/textedititemwidget.cpp b/src/plugins/qmldesigner/components/texttool/textedititemwidget.cpp index b9c5868bd89..2861344d750 100644 --- a/src/plugins/qmldesigner/components/texttool/textedititemwidget.cpp +++ b/src/plugins/qmldesigner/components/texttool/textedititemwidget.cpp @@ -38,8 +38,8 @@ void TextEditItemWidget::paint(QPainter *painter, const QStyleOptionGraphicsItem QLineEdit* TextEditItemWidget::lineEdit() const { - if (m_lineEdit.isNull()) { - m_lineEdit.reset(new QLineEdit); + if (!m_lineEdit) { + m_lineEdit = std::make_unique(); m_lineEdit->setAlignment(Qt::AlignVCenter | Qt::AlignHCenter); QPalette palette = m_lineEdit->palette(); static QColor selectionColor = Utils::creatorTheme()->color(Utils::Theme::QmlDesigner_FormEditorSelectionColor); @@ -49,13 +49,13 @@ QLineEdit* TextEditItemWidget::lineEdit() const palette.setColor(QPalette::Text, Qt::black); m_lineEdit->setPalette(palette); } - return m_lineEdit.data(); + return m_lineEdit.get(); } QTextEdit* TextEditItemWidget::textEdit() const { - if (m_textEdit.isNull()) { - m_textEdit.reset(new QTextEdit); + if (!m_textEdit) { + m_textEdit = std::make_unique(); QPalette palette = m_textEdit->palette(); static QColor selectionColor = Utils::creatorTheme()->color(Utils::Theme::QmlDesigner_FormEditorSelectionColor); palette.setColor(QPalette::Highlight, selectionColor); @@ -65,7 +65,7 @@ QTextEdit* TextEditItemWidget::textEdit() const m_textEdit->setPalette(palette); } - return m_textEdit.data(); + return m_textEdit.get(); } void TextEditItemWidget::activateTextEdit(const QSize &maximumSize) @@ -83,19 +83,19 @@ void TextEditItemWidget::activateLineEdit() QString TextEditItemWidget::text() const { - if (widget() == m_lineEdit.data()) + if (widget() == m_lineEdit.get()) return m_lineEdit->text(); - else if (widget() == m_textEdit.data()) + else if (widget() == m_textEdit.get()) return m_textEdit->toPlainText(); return QString(); } void TextEditItemWidget::updateText(const QString &text) { - if (widget() == m_lineEdit.data()) { + if (widget() == m_lineEdit.get()) { m_lineEdit->setText(text); m_lineEdit->selectAll(); - } else if (widget() == m_textEdit.data()) { + } else if (widget() == m_textEdit.get()) { m_textEdit->setText(text); m_textEdit->selectAll(); } diff --git a/src/plugins/qmldesigner/components/texttool/textedititemwidget.h b/src/plugins/qmldesigner/components/texttool/textedititemwidget.h index 8aa6c409f93..f975f1a3daa 100644 --- a/src/plugins/qmldesigner/components/texttool/textedititemwidget.h +++ b/src/plugins/qmldesigner/components/texttool/textedititemwidget.h @@ -3,7 +3,8 @@ #pragma once #include -#include + +#include QT_BEGIN_NAMESPACE class QTextEdit; @@ -32,7 +33,7 @@ protected: QString text() const; private: - mutable QScopedPointer m_lineEdit; - mutable QScopedPointer m_textEdit; + mutable std::unique_ptr m_lineEdit; + mutable std::unique_ptr m_textEdit; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/textureeditor/textureeditorqmlbackend.cpp b/src/plugins/qmldesigner/components/textureeditor/textureeditorqmlbackend.cpp index 972574554b0..415d943723d 100644 --- a/src/plugins/qmldesigner/components/textureeditor/textureeditorqmlbackend.cpp +++ b/src/plugins/qmldesigner/components/textureeditor/textureeditorqmlbackend.cpp @@ -42,10 +42,11 @@ static QObject *variantToQObject(const QVariant &value) namespace QmlDesigner { -TextureEditorQmlBackend::TextureEditorQmlBackend(TextureEditorView *textureEditor, AsynchronousImageCache &imageCache) - : m_quickWidget(new QQuickWidget) - , m_textureEditorTransaction(new TextureEditorTransaction(textureEditor)) - , m_contextObject(new TextureEditorContextObject(m_quickWidget->rootContext())) +TextureEditorQmlBackend::TextureEditorQmlBackend(TextureEditorView *textureEditor, + AsynchronousImageCache &imageCache) + : m_quickWidget(Utils::makeUniqueObjectPtr()) + , m_textureEditorTransaction(std::make_unique(textureEditor)) + , m_contextObject(std::make_unique(m_quickWidget->rootContext())) { QImage defaultImage; defaultImage.load(Utils::StyleHelper::dpiSpecificImageFile(":/textureeditor/images/texture_default.png")); @@ -56,7 +57,7 @@ TextureEditorQmlBackend::TextureEditorQmlBackend(TextureEditorView *textureEdito m_quickWidget->engine()->addImageProvider("qmldesigner_thumbnails", m_textureEditorImageProvider); m_contextObject->setBackendValues(&m_backendValuesPropertyMap); m_contextObject->setModel(textureEditor->model()); - context()->setContextObject(m_contextObject.data()); + context()->setContextObject(m_contextObject.get()); QObject::connect(&m_backendValuesPropertyMap, &DesignerPropertyMap::valueChanged, textureEditor, &TextureEditorView::changeValue); @@ -159,7 +160,7 @@ QQmlContext *TextureEditorQmlBackend::context() const TextureEditorContextObject *TextureEditorQmlBackend::contextObject() const { - return m_contextObject.data(); + return m_contextObject.get(); } QQuickWidget *TextureEditorQmlBackend::widget() const @@ -184,7 +185,7 @@ DesignerPropertyMap &TextureEditorQmlBackend::backendValuesPropertyMap() TextureEditorTransaction *TextureEditorQmlBackend::textureEditorTransaction() const { - return m_textureEditorTransaction.data(); + return m_textureEditorTransaction.get(); } PropertyEditorValue *TextureEditorQmlBackend::propertyValueForName(const QString &propertyName) @@ -227,12 +228,9 @@ void TextureEditorQmlBackend::setup(const QmlObjectNode &selectedTextureNode, co // anchors m_backendAnchorBinding.setup(selectedTextureNode.modelNode()); - context()->setContextProperties( - QVector{ - {{"anchorBackend"}, QVariant::fromValue(&m_backendAnchorBinding)}, - {{"transaction"}, QVariant::fromValue(m_textureEditorTransaction.data())} - } - ); + context()->setContextProperties(QVector{ + {{"anchorBackend"}, QVariant::fromValue(&m_backendAnchorBinding)}, + {{"transaction"}, QVariant::fromValue(m_textureEditorTransaction.get())}}); contextObject()->setSpecificsUrl(qmlSpecificsFile); contextObject()->setStateName(stateName); diff --git a/src/plugins/qmldesigner/components/textureeditor/textureeditorqmlbackend.h b/src/plugins/qmldesigner/components/textureeditor/textureeditorqmlbackend.h index c8a113f7d3d..f69c46a5698 100644 --- a/src/plugins/qmldesigner/components/textureeditor/textureeditorqmlbackend.h +++ b/src/plugins/qmldesigner/components/textureeditor/textureeditorqmlbackend.h @@ -11,6 +11,8 @@ #include +#include + class PropertyEditorValue; QT_BEGIN_NAMESPACE @@ -64,11 +66,11 @@ private: // this needs be destructed after m_quickWidget->engine() is destructed DesignerPropertyMap m_backendValuesPropertyMap; - Utils::UniqueObjectPtr m_quickWidget = nullptr; + Utils::UniqueObjectPtr m_quickWidget; QmlAnchorBindingProxy m_backendAnchorBinding; QmlModelNodeProxy m_backendModelNode; - QScopedPointer m_textureEditorTransaction; - QScopedPointer m_contextObject; + std::unique_ptr m_textureEditorTransaction; + std::unique_ptr m_contextObject; AssetImageProvider *m_textureEditorImageProvider = nullptr; }; diff --git a/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp b/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp index 5de3730c97b..a637431c4dd 100644 --- a/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp +++ b/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp @@ -43,7 +43,6 @@ #include #include #include -#include #include #include #include @@ -698,7 +697,8 @@ void TextureEditorView::selectedNodesChanged(const QList &selectedNod m_selectedModel = selectedNodeList.at(0); bool hasValidSelection = QmlObjectNode(m_selectedModel).hasBindingProperty("materials"); - m_qmlBackEnd->contextObject()->setHasSingleModelSelection(hasValidSelection); + if (m_qmlBackEnd) + m_qmlBackEnd->contextObject()->setHasSingleModelSelection(hasValidSelection); } void TextureEditorView::currentStateChanged(const ModelNode &node) diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelinepropertyitem.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelinepropertyitem.cpp index 698ef0f03bb..762bd85c8b4 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelinepropertyitem.cpp +++ b/src/plugins/qmldesigner/components/timelineeditor/timelinepropertyitem.cpp @@ -35,7 +35,6 @@ #include #include #include -#include #include diff --git a/src/plugins/qmldesigner/designercore/generatedcomponentutils.cpp b/src/plugins/qmldesigner/designercore/generatedcomponentutils.cpp index 5ee5790b534..da40c4d387d 100644 --- a/src/plugins/qmldesigner/designercore/generatedcomponentutils.cpp +++ b/src/plugins/qmldesigner/designercore/generatedcomponentutils.cpp @@ -2,11 +2,36 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "generatedcomponentutils.h" - #include namespace QmlDesigner { +bool couldBeProjectModule(const Utils::FilePath &path, const QString &projectName) +{ + if (!path.exists()) + return false; + + Utils::FilePath qmlDirPath = path.pathAppended("qmldir"); + if (qmlDirPath.exists()) { + Utils::expected_str qmldirContents = qmlDirPath.fileContents(); + if (!qmldirContents.has_value()) + return false; + + const QString expectedLine = QLatin1String("module %1").arg(projectName); + QByteArray fileContents = *qmldirContents; + QTextStream stream(fileContents); + while (!stream.atEnd()) { + QString lineData = stream.readLine().trimmed(); + if (lineData.startsWith(u"module ")) + return lineData == expectedLine; + } + } + if (path.endsWith(projectName)) + return true; + + return false; +} + GeneratedComponentUtils::GeneratedComponentUtils(ExternalDependenciesInterface &externalDependencies) : m_externalDependencies(externalDependencies) { @@ -60,7 +85,10 @@ Utils::FilePath GeneratedComponentUtils::componentBundlesBasePath() const if (basePath.isEmpty()) return {}; - return basePath.resolvePath(QLatin1String(Constants::COMPONENT_BUNDLES_TYPE)); + if (basePath.endsWith(Constants::GENERATED_COMPONENTS_FOLDER)) + return basePath.resolvePath(QLatin1String(Constants::COMPONENT_BUNDLES_TYPE)); + + return basePath.resolvePath(QLatin1String(Constants::OLD_COMPONENT_BUNDLES_TYPE)); } Utils::FilePath GeneratedComponentUtils::import3dBasePath() const @@ -77,6 +105,62 @@ Utils::FilePath GeneratedComponentUtils::import3dBasePath() const return basePath.resolvePath(QLatin1String(Constants::QUICK_3D_COMPONENTS_FOLDER)); } +Utils::FilePath GeneratedComponentUtils::materialBundlePath() const +{ + Utils::FilePath basePath = componentBundlesBasePath(); + + if (basePath.isEmpty()) + return {}; + + if (basePath.endsWith(Constants::OLD_COMPONENT_BUNDLES_TYPE)) + return basePath.resolvePath(QLatin1String(Constants::OLD_COMPONENT_BUNDLES_MATERIAL_BUNDLE_TYPE)); + + return basePath.resolvePath(QLatin1String(Constants::COMPONENT_BUNDLES_MATERIAL_BUNDLE_TYPE)); +} + +Utils::FilePath GeneratedComponentUtils::effectBundlePath() const +{ + Utils::FilePath basePath = componentBundlesBasePath(); + + if (basePath.isEmpty()) + return {}; + + if (basePath.endsWith(Constants::OLD_COMPONENT_BUNDLES_TYPE)) + return basePath.resolvePath(QLatin1String(Constants::OLD_COMPONENT_BUNDLES_EFFECT_BUNDLE_TYPE)); + + return basePath.resolvePath(QLatin1String(Constants::COMPONENT_BUNDLES_EFFECT_BUNDLE_TYPE)); +} + +Utils::FilePath GeneratedComponentUtils::projectModulePath(bool generateIfNotExists) const +{ + using Utils::FilePath; + FilePath projectPath = FilePath::fromString(m_externalDependencies.currentProjectDirPath()); + + if (projectPath.isEmpty()) + return {}; + + const QString projectName = m_externalDependencies.projectName(); + + FilePath newImportDirectory = projectPath.pathAppended(projectName); + if (couldBeProjectModule(newImportDirectory, projectName)) + return newImportDirectory; + + FilePath oldImportDirectory = projectPath.resolvePath(QLatin1String("imports/") + projectName); + if (couldBeProjectModule(oldImportDirectory, projectName)) + return oldImportDirectory; + + for (const QString &path : m_externalDependencies.projectModulePaths()) { + FilePath dir = FilePath::fromString(path); + if (couldBeProjectModule(dir, projectName)) + return dir; + } + + if (generateIfNotExists) + newImportDirectory.createDir(); + + return newImportDirectory; +} + bool GeneratedComponentUtils::isImport3dPath(const QString &path) const { return path.contains('/' + QLatin1String(Constants::OLD_QUICK_3D_ASSETS_FOLDER)) @@ -87,9 +171,21 @@ bool GeneratedComponentUtils::isImport3dPath(const QString &path) const bool GeneratedComponentUtils::isComposedEffectPath(const QString &path) const { return path.contains(Constants::OLD_EFFECTS_IMPORT_FOLDER) - || path.contains('/' + QLatin1String(Constants::COMPOSED_EFFECTS_TYPE)); + || path.contains(QLatin1String(Constants::GENERATED_COMPONENTS_FOLDER) + '/' + + QLatin1String(Constants::COMPOSED_EFFECTS_TYPE)); } +bool GeneratedComponentUtils::isBundlePath(const QString &path) const +{ + return path.contains(componentBundlesTypePrefix().replace('.', '/')); +} + +bool GeneratedComponentUtils::isGeneratedPath(const QString &path) const +{ + return path.startsWith(generatedComponentsPath().toFSPathString()); +} + + QString GeneratedComponentUtils::generatedComponentTypePrefix() const { Utils::FilePath basePath = generatedComponentsPath(); @@ -123,7 +219,7 @@ QString GeneratedComponentUtils::componentBundlesTypePrefix() const if (basePrefix.endsWith(Constants::GENERATED_COMPONENTS_FOLDER)) return basePrefix + '.' + QLatin1String(Constants::COMPONENT_BUNDLES_TYPE); - return Constants::COMPONENT_BUNDLES_TYPE; + return Constants::OLD_COMPONENT_BUNDLES_TYPE; } QString GeneratedComponentUtils::composedEffectsTypePrefix() const @@ -136,4 +232,60 @@ QString GeneratedComponentUtils::composedEffectsTypePrefix() const return Constants::OLD_EFFECTS_FOLDER; } +QString GeneratedComponentUtils::materialsBundleId() const +{ + bool isNewImportDir = generatedComponentTypePrefix().endsWith(Constants::GENERATED_COMPONENTS_FOLDER); + + return QLatin1String(isNewImportDir ? Constants::COMPONENT_BUNDLES_MATERIAL_BUNDLE_TYPE + : Constants::OLD_COMPONENT_BUNDLES_MATERIAL_BUNDLE_TYPE); +} + +QString GeneratedComponentUtils::effectsBundleId() const +{ + bool isNewImportDir = generatedComponentTypePrefix().endsWith(Constants::GENERATED_COMPONENTS_FOLDER); + + return QLatin1String(isNewImportDir ? Constants::COMPONENT_BUNDLES_EFFECT_BUNDLE_TYPE + : Constants::OLD_COMPONENT_BUNDLES_EFFECT_BUNDLE_TYPE); +} + +QString GeneratedComponentUtils::userMaterialsBundleId() const +{ + return QLatin1String(Constants::COMPONENT_BUNDLES_USER_MATERIAL_BUNDLE_TYPE); +} + +QString GeneratedComponentUtils::userEffectsBundleId() const +{ + return QLatin1String(Constants::COMPONENT_BUNDLES_USER_EFFECT_BUNDLE_TYPE); +} + +QString GeneratedComponentUtils::user3DBundleId() const +{ + return QLatin1String(Constants::COMPONENT_BUNDLES_USER_3D_BUNDLE_TYPE); +} + +QString GeneratedComponentUtils::materialsBundleType() const +{ + return componentBundlesTypePrefix() + '.' + materialsBundleId(); +} + +QString GeneratedComponentUtils::effectsBundleType() const +{ + return componentBundlesTypePrefix() + '.' + effectsBundleId(); +} + +QString GeneratedComponentUtils::userMaterialsBundleType() const +{ + return componentBundlesTypePrefix() + '.' + userMaterialsBundleId(); +} + +QString GeneratedComponentUtils::userEffectsBundleType() const +{ + return componentBundlesTypePrefix() + '.' + userEffectsBundleId(); +} + +QString GeneratedComponentUtils::user3DBundleType() const +{ + return componentBundlesTypePrefix() + '.' + user3DBundleId(); +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/generatedcomponentutils.h b/src/plugins/qmldesigner/designercore/generatedcomponentutils.h index e2e9baf3e75..ceddb405a34 100644 --- a/src/plugins/qmldesigner/designercore/generatedcomponentutils.h +++ b/src/plugins/qmldesigner/designercore/generatedcomponentutils.h @@ -21,9 +21,14 @@ public: Utils::FilePath composedEffectPath(const QString &effectPath) const; Utils::FilePath componentBundlesBasePath() const; Utils::FilePath import3dBasePath() const; + Utils::FilePath materialBundlePath() const; + Utils::FilePath effectBundlePath() const; + Utils::FilePath projectModulePath(bool generateIfNotExists = false) const; bool isImport3dPath(const QString &path) const; bool isComposedEffectPath(const QString &path) const; + bool isBundlePath(const QString &path) const; + bool isGeneratedPath(const QString &path) const; QString generatedComponentTypePrefix() const; QString import3dTypePrefix() const; @@ -31,6 +36,18 @@ public: QString componentBundlesTypePrefix() const; QString composedEffectsTypePrefix() const; + QString materialsBundleId() const; + QString effectsBundleId() const; + QString userMaterialsBundleId() const; + QString userEffectsBundleId() const; + QString user3DBundleId() const; + + QString materialsBundleType() const; + QString effectsBundleType() const; + QString userMaterialsBundleType() const; + QString userEffectsBundleType() const; + QString user3DBundleType() const; + private: ExternalDependenciesInterface &m_externalDependencies; }; diff --git a/src/plugins/qmldesigner/designercore/include/externaldependenciesinterface.h b/src/plugins/qmldesigner/designercore/include/externaldependenciesinterface.h index 71ddeb7dc19..9055f51b6a1 100644 --- a/src/plugins/qmldesigner/designercore/include/externaldependenciesinterface.h +++ b/src/plugins/qmldesigner/designercore/include/externaldependenciesinterface.h @@ -28,6 +28,7 @@ public: virtual QString qmlPuppetFallbackDirectory() const = 0; virtual QString defaultPuppetToplevelBuildDirectory() const = 0; virtual QUrl projectUrl() const = 0; + virtual QString projectName() const = 0; virtual QString currentProjectDirPath() const = 0; virtual QUrl currentResourcePath() const = 0; virtual void parseItemLibraryDescriptions() = 0; diff --git a/src/plugins/qmldesigner/designercore/include/itemlibraryentry.h b/src/plugins/qmldesigner/designercore/include/itemlibraryentry.h index f88f9e35c6f..2d0f2ef31e5 100644 --- a/src/plugins/qmldesigner/designercore/include/itemlibraryentry.h +++ b/src/plugins/qmldesigner/designercore/include/itemlibraryentry.h @@ -42,13 +42,16 @@ class QMLDESIGNERCORE_EXPORT ItemLibraryEntry public: ItemLibraryEntry(); - explicit ItemLibraryEntry(const Storage::Info::ItemLibraryEntry &entry, - const ProjectStorageType &projectStorage); - ~ItemLibraryEntry() = default; + ItemLibraryEntry(const ItemLibraryEntry &) = default; + ItemLibraryEntry &operator=(const ItemLibraryEntry &) = default; + ItemLibraryEntry(ItemLibraryEntry &&) = default; + ItemLibraryEntry &operator=(ItemLibraryEntry &&) = default; + explicit ItemLibraryEntry(const Storage::Info::ItemLibraryEntry &entry); + ~ItemLibraryEntry(); QString name() const; TypeName typeName() const; - const NodeMetaInfo &metaInfo() const; + TypeId typeId() const; QIcon typeIcon() const; QString libraryEntryIconPath() const; int majorVersion() const; @@ -86,7 +89,7 @@ private: using ItemLibraryEntries = QList; QMLDESIGNERCORE_EXPORT QList toItemLibraryEntries( - const Storage::Info::ItemLibraryEntries &entries, const ProjectStorageType &projectStorage); + const Storage::Info::ItemLibraryEntries &entries); } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/include/model.h b/src/plugins/qmldesigner/designercore/include/model.h index 85f129cdbce..39b5cdaa811 100644 --- a/src/plugins/qmldesigner/designercore/include/model.h +++ b/src/plugins/qmldesigner/designercore/include/model.h @@ -137,7 +137,7 @@ public: ModelPointer createModel(const TypeName &typeName, std::unique_ptr resourceManagement = {}); - QUrl fileUrl() const; + const QUrl &fileUrl() const; SourceId fileUrlSourceId() const; void setFileUrl(const QUrl &url); @@ -147,7 +147,7 @@ public: void setMetaInfo(const MetaInfo &metaInfo); #endif - Module module(Utils::SmallStringView moduleName); + Module module(Utils::SmallStringView moduleName, Storage::ModuleKind moduleKind); NodeMetaInfo metaInfo(const TypeName &typeName, int majorVersion = -1, int minorVersion = -1) const; NodeMetaInfo metaInfo(Module module, Utils::SmallStringView typeName, @@ -255,10 +255,7 @@ public: bool hasId(const QString &id) const; bool hasImport(const QString &importUrl) const; - QString generateNewId(const QString &prefixName, - const QString &fallbackPrefix = "element", - std::optional> isDuplicate = {}) const; - QString generateIdFromName(const QString &name, const QString &fallbackId = "element") const; + QString generateNewId(const QString &prefixName, const QString &fallbackPrefix = "element") const; void startDrag(QMimeData *mimeData, const QPixmap &icon); void endDrag(); diff --git a/src/plugins/qmldesigner/designercore/include/nodehints.h b/src/plugins/qmldesigner/designercore/include/nodehints.h index 9e67c2d99b3..99470db65f1 100644 --- a/src/plugins/qmldesigner/designercore/include/nodehints.h +++ b/src/plugins/qmldesigner/designercore/include/nodehints.h @@ -3,9 +3,11 @@ #pragma once +#include "modelnode.h" +#include "nodemetainfo.h" + #include #include -#include "modelnode.h" #include "qmldesignercorelib_global.h" #include "invalidmetainfoexception.h" @@ -54,18 +56,19 @@ public: QHash hints() const; static NodeHints fromModelNode(const ModelNode &modelNode); - static NodeHints fromItemLibraryEntry(const ItemLibraryEntry &entry); + static NodeHints fromItemLibraryEntry(const ItemLibraryEntry &entry, Model *model); private: explicit NodeHints(const ModelNode &modelNode); explicit NodeHints(const NodeMetaInfo &metaInfo); - explicit NodeHints(const ItemLibraryEntry &entry); + explicit NodeHints(const ItemLibraryEntry &entry, Model *model); const ModelNode &modelNode() const; bool isValid() const; Model *model() const; bool evaluateBooleanExpression(const QString &hintName, bool defaultValue, const ModelNode potentialParent = ModelNode()) const; ModelNode m_modelNode; + NodeMetaInfo m_metaInfo; QHash m_hints; }; diff --git a/src/plugins/qmldesigner/designercore/include/nodemetainfo.h b/src/plugins/qmldesigner/designercore/include/nodemetainfo.h index ba2e2cda65e..fd3f2f9be8d 100644 --- a/src/plugins/qmldesigner/designercore/include/nodemetainfo.h +++ b/src/plugins/qmldesigner/designercore/include/nodemetainfo.h @@ -28,10 +28,14 @@ QT_END_NAMESPACE [[deprecated( \ "In most cases you don't need them anymore because the import is setting them!")]] # define DEPRECATED_COMPONENT_FILE_NAME [[deprecated("Use sourceId() instead.")]] +# define DEPRECATED_IMPORT_DIRECTORY_PATH [[deprecated("Use allExportedTypeNames().")]] +# define DEPRECATED_REQUIRED_IMPORT_STRING [[deprecated("Use allExportedTypeNames().")]] #else # define DEPRECATED_TYPENAME # define DEPRECATED_VERSION_NUMBER # define DEPRECATED_COMPONENT_FILE_NAME +# define DEPRECATED_IMPORT_DIRECTORY_PATH +# define DEPRECATED_REQUIRED_IMPORT_STRING #endif namespace QmlDesigner { @@ -181,6 +185,7 @@ public: bool isQtQuick3DLight() const; bool isQtQuickListModel() const; bool isQtQuickListView() const; + bool isQtQuickGridView() const; bool isQtQuick3DMaterial() const; bool isQtQuick3DModel() const; bool isQtQuick3DNode() const; @@ -237,8 +242,8 @@ public: bool usesCustomParser() const; bool isEnumeration() const; - QString importDirectoryPath() const; - QString requiredImportString() const; + DEPRECATED_IMPORT_DIRECTORY_PATH QString importDirectoryPath() const; + DEPRECATED_REQUIRED_IMPORT_STRING QString requiredImportString() const; friend bool operator==(const NodeMetaInfo &first, const NodeMetaInfo &second) { diff --git a/src/plugins/qmldesigner/designercore/include/projectstorageids.h b/src/plugins/qmldesigner/designercore/include/projectstorageids.h index 0b157e55e77..7c2e8c4b252 100644 --- a/src/plugins/qmldesigner/designercore/include/projectstorageids.h +++ b/src/plugins/qmldesigner/designercore/include/projectstorageids.h @@ -48,6 +48,8 @@ using EnumerationDeclarationIds = std::vector; using SourceContextId = Sqlite::BasicId; using SourceContextIds = std::vector; +template +using SmallSourceContextIds = QVarLengthArray; using SourceId = Sqlite::BasicId; using SourceIds = std::vector; diff --git a/src/plugins/qmldesigner/designercore/include/rewriterview.h b/src/plugins/qmldesigner/designercore/include/rewriterview.h index 0134349682e..92c79c78631 100644 --- a/src/plugins/qmldesigner/designercore/include/rewriterview.h +++ b/src/plugins/qmldesigner/designercore/include/rewriterview.h @@ -8,11 +8,11 @@ #include "documentmessage.h" #include "rewritertransaction.h" -#include #include #include #include +#include namespace QmlJS { class Document; @@ -203,9 +203,9 @@ private: //variables bool m_checkLinkErrors = true; DifferenceHandling m_differenceHandling; - QScopedPointer m_positionStorage; - QScopedPointer m_modelToTextMerger; - QScopedPointer m_textToModelMerger; + std::unique_ptr m_positionStorage; + std::unique_ptr m_modelToTextMerger; + std::unique_ptr m_textToModelMerger; QList m_errors; QList m_warnings; RewriterTransaction m_removeDefaultPropertyTransaction; diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp index 1b965db66a7..c033c983316 100644 --- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp +++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp @@ -91,7 +91,6 @@ #include #include #include -#include #include #include @@ -1031,8 +1030,20 @@ TypeName createQualifiedTypeName(const ModelNode &node) auto exportedTypes = node.metaInfo().exportedTypeNamesForSourceId(model->fileUrlSourceId()); if (exportedTypes.size()) { const auto &exportedType = exportedTypes.front(); - Utils::PathString typeName = model->projectStorage()->moduleName(exportedType.moduleId); - typeName += '/'; + using Storage::ModuleKind; + auto module = model->projectStorage()->module(exportedType.moduleId); + Utils::PathString typeName; + switch (module.kind) { + case ModuleKind::QmlLibrary: + typeName += module.name; + typeName += '/'; + break; + case ModuleKind::PathLibrary: + break; + case ModuleKind::CppLibrary: + break; + } + typeName += exportedType.name; return typeName.toQByteArray(); @@ -1205,6 +1216,13 @@ CreateSceneCommand NodeInstanceView::createCreateSceneCommand() if (stateNode.isValid() && stateNode.metaInfo().isQtQuickState()) stateInstanceId = stateNode.internalId(); + QHash sceneStates = m_edit3DToolStates[model()->fileUrl()]; + QHash projectStates = m_edit3DToolStates[ + QUrl::fromLocalFile(m_externalDependencies.currentProjectDirPath())]; + const QString ptsId = "@PTS"; + if (projectStates.contains(ptsId)) + sceneStates.insert(ptsId, projectStates[ptsId]); + return CreateSceneCommand(instanceContainerList, reparentContainerList, idContainerList, @@ -1215,7 +1233,7 @@ CreateSceneCommand NodeInstanceView::createCreateSceneCommand() mockupTypesVector, model()->fileUrl(), m_externalDependencies.currentResourcePath(), - m_edit3DToolStates[model()->fileUrl()], + sceneStates, lastUsedLanguage, m_captureImageMinimumSize, m_captureImageMaximumSize, @@ -1733,7 +1751,12 @@ void NodeInstanceView::handlePuppetToCreatorCommand(const PuppetToCreatorCommand auto data = qvariant_cast(command.data()); if (data.size() == 3) { QString qmlId = data[0].toString(); - m_edit3DToolStates[model()->fileUrl()][qmlId].insert(data[1].toString(), data[2]); + QUrl mainKey; + if (qmlId == "@PTS") // Project tool state + mainKey = QUrl::fromLocalFile(m_externalDependencies.currentProjectDirPath()); + else + mainKey = model()->fileUrl(); + m_edit3DToolStates[mainKey][qmlId].insert(data[1].toString(), data[2]); } } } else if (command.type() == PuppetToCreatorCommand::Render3DView) { diff --git a/src/plugins/qmldesigner/designercore/metainfo/itemlibraryentry.cpp b/src/plugins/qmldesigner/designercore/metainfo/itemlibraryentry.cpp index 806da7e7c4d..2aec7660026 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/itemlibraryentry.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/itemlibraryentry.cpp @@ -22,7 +22,7 @@ class ItemLibraryEntryData public: QString name; TypeName typeName; - NodeMetaInfo metaInfo; + TypeId typeId; QString category; int majorVersion{-1}; int minorVersion{-1}; @@ -64,12 +64,12 @@ ItemLibraryEntry::ItemLibraryEntry() : m_data(std::make_shared()) {} -ItemLibraryEntry::ItemLibraryEntry(const Storage::Info::ItemLibraryEntry &entry, - const ProjectStorageType &projectStorage) +ItemLibraryEntry::ItemLibraryEntry(const Storage::Info::ItemLibraryEntry &entry) : ItemLibraryEntry{} { m_data->name = entry.name.toQString(); - m_data->metaInfo = {entry.typeId, &projectStorage}; + m_data->typeId = entry.typeId; + m_data->typeName = entry.typeName.toQByteArray(); m_data->category = entry.category.toQString(); if (entry.iconPath.size()) m_data->libraryEntryIconPath = entry.iconPath.toQString(); @@ -87,6 +87,8 @@ ItemLibraryEntry::ItemLibraryEntry(const Storage::Info::ItemLibraryEntry &entry, m_data->extraFilePaths.emplace_back(extraFilePath.toQString()); } +ItemLibraryEntry::~ItemLibraryEntry() = default; + QString ItemLibraryEntry::name() const { return m_data->name; @@ -97,9 +99,9 @@ TypeName ItemLibraryEntry::typeName() const return m_data->typeName; } -const NodeMetaInfo &ItemLibraryEntry::metaInfo() const +TypeId ItemLibraryEntry::typeId() const { - return m_data->metaInfo; + return m_data->typeId; } QString ItemLibraryEntry::qmlSource() const @@ -245,6 +247,7 @@ QDataStream &operator<<(QDataStream &stream, const ItemLibraryEntry &itemLibrary stream << itemLibraryEntry.m_data->qmlSource; stream << itemLibraryEntry.m_data->customComponentSource; stream << itemLibraryEntry.m_data->extraFilePaths; + stream << itemLibraryEntry.m_data->typeId.internalId(); return stream; } @@ -270,6 +273,9 @@ QDataStream &operator>>(QDataStream &stream, ItemLibraryEntry &itemLibraryEntry) stream >> itemLibraryEntry.m_data->qmlSource; stream >> itemLibraryEntry.m_data->customComponentSource; stream >> itemLibraryEntry.m_data->extraFilePaths; + TypeId::DatabaseType internalTypeId; + stream >> internalTypeId; + itemLibraryEntry.m_data->typeId = TypeId::create(internalTypeId); return stream; } @@ -295,11 +301,10 @@ QDebug operator<<(QDebug debug, const ItemLibraryEntry &itemLibraryEntry) return debug.space(); } -QList toItemLibraryEntries(const Storage::Info::ItemLibraryEntries &entries, - const ProjectStorageType &projectStorage) +QList toItemLibraryEntries(const Storage::Info::ItemLibraryEntries &entries) { return Utils::transform>(entries, [&](const auto &entry) { - return ItemLibraryEntry{entry, projectStorage}; + return ItemLibraryEntry{entry}; }); } diff --git a/src/plugins/qmldesigner/designercore/metainfo/nodehints.cpp b/src/plugins/qmldesigner/designercore/metainfo/nodehints.cpp index 32e68a3cdc6..1f9a3e42bd6 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/nodehints.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/nodehints.cpp @@ -106,14 +106,15 @@ QmlDesigner::NodeHints::NodeHints(const ModelNode &node) } NodeHints::NodeHints(const NodeMetaInfo &metaInfo) + : m_metaInfo{metaInfo} { for (const auto &[name, expression] : metaInfo.typeHints()) m_hints.insert(name.toQString(), expression.toQString()); } -NodeHints::NodeHints(const ItemLibraryEntry &entry) +NodeHints::NodeHints(const ItemLibraryEntry &entry, [[maybe_unused]] Model *model) #ifdef QDS_USE_PROJECTSTORAGE - : NodeHints{entry.metaInfo()} + : NodeHints{NodeMetaInfo{entry.typeId(), model->projectStorage()}} #endif { if constexpr (!useProjectStorage()) @@ -135,7 +136,7 @@ bool NodeHints::canBeContainerFor(const ModelNode &potenialChild) const if (!isValid()) return true; - auto flagIs = m_modelNode.metaInfo().canBeContainer(); + auto flagIs = m_metaInfo.canBeContainer(); if (flagIs != FlagIs::Set) return convert(flagIs); @@ -151,7 +152,7 @@ bool NodeHints::forceClip() const if (isSwipeView(modelNode())) return true; - auto flagIs = m_modelNode.metaInfo().forceClip(); + auto flagIs = m_metaInfo.forceClip(); if (flagIs != FlagIs::Set) return convert(flagIs); @@ -167,7 +168,7 @@ bool NodeHints::doesLayoutChildren() const if (isSwipeView(modelNode())) return true; - auto flagIs = m_modelNode.metaInfo().doesLayoutChildren(); + auto flagIs = m_metaInfo.doesLayoutChildren(); if (flagIs != FlagIs::Set) return convert(flagIs); @@ -177,7 +178,7 @@ bool NodeHints::doesLayoutChildren() const bool NodeHints::canBeDroppedInFormEditor() const { - auto flagIs = m_modelNode.metaInfo().canBeDroppedInFormEditor(); + auto flagIs = m_metaInfo.canBeDroppedInFormEditor(); if (flagIs != FlagIs::Set) return convert(flagIs); @@ -187,7 +188,7 @@ bool NodeHints::canBeDroppedInFormEditor() const bool NodeHints::canBeDroppedInNavigator() const { - auto flagIs = m_modelNode.metaInfo().canBeDroppedInNavigator(); + auto flagIs = m_metaInfo.canBeDroppedInNavigator(); if (flagIs != FlagIs::Set) return convert(flagIs); @@ -197,7 +198,7 @@ bool NodeHints::canBeDroppedInNavigator() const bool NodeHints::canBeDroppedInView3D() const { - auto flagIs = m_modelNode.metaInfo().canBeDroppedInView3D(); + auto flagIs = m_metaInfo.canBeDroppedInView3D(); if (flagIs != FlagIs::Set) return convert(flagIs); @@ -210,7 +211,7 @@ bool NodeHints::isMovable() const if (!isValid()) return true; - auto flagIs = m_modelNode.metaInfo().isMovable(); + auto flagIs = m_metaInfo.isMovable(); if (flagIs != FlagIs::Set) return convert(flagIs); @@ -223,7 +224,7 @@ bool NodeHints::isResizable() const if (!isValid()) return true; - auto flagIs = m_modelNode.metaInfo().isResizable(); + auto flagIs = m_metaInfo.isResizable(); if (flagIs != FlagIs::Set) return convert(flagIs); @@ -236,7 +237,7 @@ bool NodeHints::hasFormEditorItem() const if (!isValid()) return true; - auto flagIs = m_modelNode.metaInfo().hasFormEditorItem(); + auto flagIs = m_metaInfo.hasFormEditorItem(); if (flagIs != FlagIs::Set) return convert(flagIs); @@ -252,7 +253,7 @@ bool NodeHints::isStackedContainer() const if (isSwipeView(modelNode())) return true; - auto flagIs = m_modelNode.metaInfo().isStackedContainer(); + auto flagIs = m_metaInfo.isStackedContainer(); if (flagIs != FlagIs::Set) return convert(flagIs); @@ -299,7 +300,7 @@ bool NodeHints::takesOverRenderingOfChildren() const if (!isValid()) return false; - auto flagIs = m_modelNode.metaInfo().takesOverRenderingOfChildren(); + auto flagIs = m_metaInfo.takesOverRenderingOfChildren(); if (flagIs != FlagIs::Set) return convert(flagIs); @@ -312,7 +313,7 @@ bool NodeHints::visibleInNavigator() const if (!isValid()) return false; - auto flagIs = m_modelNode.metaInfo().visibleInNavigator(); + auto flagIs = m_metaInfo.visibleInNavigator(); if (flagIs != FlagIs::Set) return convert(flagIs); @@ -322,7 +323,7 @@ bool NodeHints::visibleInNavigator() const bool NodeHints::visibleInLibrary() const { - auto flagIs = m_modelNode.metaInfo().visibleInLibrary(); + auto flagIs = m_metaInfo.visibleInLibrary(); if (flagIs != FlagIs::Set) return convert(flagIs); @@ -391,9 +392,9 @@ NodeHints NodeHints::fromModelNode(const ModelNode &modelNode) return NodeHints(modelNode); } -NodeHints NodeHints::fromItemLibraryEntry(const ItemLibraryEntry &entry) +NodeHints NodeHints::fromItemLibraryEntry(const ItemLibraryEntry &entry, Model *model) { - return NodeHints(entry); + return NodeHints(entry, model); } const ModelNode &NodeHints::modelNode() const diff --git a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp index 3e9513ae3c7..c6566342728 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp @@ -53,6 +53,10 @@ NodeMetaInfo object will result in an InvalidMetaInfoException being thrown. namespace { +using Storage::ModuleKind; + +auto category = MetaInfoTracing::category; + struct TypeDescription { QString className; @@ -1493,15 +1497,20 @@ MetaInfoType NodeMetaInfo::type() const { if constexpr (useProjectStorage()) { if (isValid()) { - switch (typeData().traits.kind) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get type"_t, category(), keyValue("type id", m_typeId)}; + auto kind = typeData().traits.kind; + tracer.end(keyValue("type kind", kind)); + + switch (kind) { case Storage::TypeTraitsKind::Reference: return MetaInfoType::Reference; case Storage::TypeTraitsKind::Value: return MetaInfoType::Value; case Storage::TypeTraitsKind::Sequence: return MetaInfoType::Sequence; - default: - break; + case Storage::TypeTraitsKind::None: + return MetaInfoType::None; } } } @@ -1511,16 +1520,38 @@ MetaInfoType NodeMetaInfo::type() const bool NodeMetaInfo::isFileComponent() const { - if constexpr (useProjectStorage()) - return isValid() && typeData().traits.isFileComponent; - else + if constexpr (useProjectStorage()) { + if (!isValid()) + return {}; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is file component"_t, category(), keyValue("type id", m_typeId)}; + + auto isFileComponent = typeData().traits.isFileComponent; + + tracer.end(keyValue("is file component", isFileComponent)); + + return isFileComponent; + + } else { return isValid() && m_privateData->isFileComponent(); + } } bool NodeMetaInfo::isProjectComponent() const { if constexpr (useProjectStorage()) { - return isValid() && typeData().traits.isProjectComponent; + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is project component"_t, category(), keyValue("type id", m_typeId)}; + + auto isProjectComponent = typeData().traits.isProjectComponent; + + tracer.end(keyValue("is project component", isProjectComponent)); + + return isProjectComponent; } return false; @@ -1529,7 +1560,17 @@ bool NodeMetaInfo::isProjectComponent() const bool NodeMetaInfo::isInProjectModule() const { if constexpr (useProjectStorage()) { - return isValid() && typeData().traits.isInProjectModule; + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is project module"_t, category(), keyValue("type id", m_typeId)}; + + auto isInProjectModule = typeData().traits.isInProjectModule; + + tracer.end(keyValue("is project module", isInProjectModule)); + + return isInProjectModule; } return false; @@ -1538,10 +1579,17 @@ bool NodeMetaInfo::isInProjectModule() const FlagIs NodeMetaInfo::canBeContainer() const { if constexpr (useProjectStorage()) { - if (isValid()) - return typeData().traits.canBeContainer; + if (!isValid()) + return FlagIs::False; - return FlagIs::False; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"can be container"_t, category(), keyValue("type id", m_typeId)}; + + auto canBeContainer = typeData().traits.canBeContainer; + + tracer.end(keyValue("can be container", canBeContainer)); + + return canBeContainer; } return FlagIs::Set; @@ -1550,10 +1598,17 @@ FlagIs NodeMetaInfo::canBeContainer() const FlagIs NodeMetaInfo::forceClip() const { if constexpr (useProjectStorage()) { - if (isValid()) - return typeData().traits.forceClip; + if (!isValid()) + return FlagIs::False; - return FlagIs::False; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"force clip"_t, category(), keyValue("type id", m_typeId)}; + + auto forceClip = typeData().traits.forceClip; + + tracer.end(keyValue("force clip", forceClip)); + + return forceClip; } return FlagIs::Set; @@ -1562,10 +1617,17 @@ FlagIs NodeMetaInfo::forceClip() const FlagIs NodeMetaInfo::doesLayoutChildren() const { if constexpr (useProjectStorage()) { - if (isValid()) - return typeData().traits.doesLayoutChildren; + if (!isValid()) + return FlagIs::False; - return FlagIs::False; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"does layout children"_t, category(), keyValue("type id", m_typeId)}; + + auto doesLayoutChildren = typeData().traits.doesLayoutChildren; + + tracer.end(keyValue("does layout children", doesLayoutChildren)); + + return doesLayoutChildren; } return FlagIs::Set; @@ -1574,10 +1636,19 @@ FlagIs NodeMetaInfo::doesLayoutChildren() const FlagIs NodeMetaInfo::canBeDroppedInFormEditor() const { if constexpr (useProjectStorage()) { - if (isValid()) - return typeData().traits.canBeDroppedInFormEditor; + if (!isValid()) + return FlagIs::False; - return FlagIs::False; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"can be dropped in form editor"_t, + category(), + keyValue("type id", m_typeId)}; + + auto canBeDroppedInFormEditor = typeData().traits.canBeDroppedInFormEditor; + + tracer.end(keyValue("can be dropped in form editor", canBeDroppedInFormEditor)); + + return canBeDroppedInFormEditor; } return FlagIs::Set; @@ -1586,10 +1657,19 @@ FlagIs NodeMetaInfo::canBeDroppedInFormEditor() const FlagIs NodeMetaInfo::canBeDroppedInNavigator() const { if constexpr (useProjectStorage()) { - if (isValid()) - return typeData().traits.canBeDroppedInNavigator; + if (!isValid()) + return FlagIs::False; - return FlagIs::False; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"can be dropped in navigator"_t, + category(), + keyValue("type id", m_typeId)}; + + auto canBeDroppedInNavigator = typeData().traits.canBeDroppedInNavigator; + + tracer.end(keyValue("can be dropped in navigator", canBeDroppedInNavigator)); + + return canBeDroppedInNavigator; } return FlagIs::Set; @@ -1598,10 +1678,19 @@ FlagIs NodeMetaInfo::canBeDroppedInNavigator() const FlagIs NodeMetaInfo::canBeDroppedInView3D() const { if constexpr (useProjectStorage()) { - if (isValid()) - return typeData().traits.canBeDroppedInView3D; + if (!isValid()) + return FlagIs::False; - return FlagIs::False; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"can be dropped in view3d"_t, + category(), + keyValue("type id", m_typeId)}; + + auto canBeDroppedInView3D = typeData().traits.canBeDroppedInView3D; + + tracer.end(keyValue("can be dropped in view3d", canBeDroppedInView3D)); + + return canBeDroppedInView3D; } return FlagIs::Set; @@ -1610,10 +1699,17 @@ FlagIs NodeMetaInfo::canBeDroppedInView3D() const FlagIs NodeMetaInfo::isMovable() const { if constexpr (useProjectStorage()) { - if (isValid()) - return typeData().traits.isMovable; + if (!isValid()) + return FlagIs::False; - return FlagIs::False; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is movable"_t, category(), keyValue("type id", m_typeId)}; + + auto isMovable = typeData().traits.isMovable; + + tracer.end(keyValue("is movable", isMovable)); + + return isMovable; } return FlagIs::Set; @@ -1622,10 +1718,17 @@ FlagIs NodeMetaInfo::isMovable() const FlagIs NodeMetaInfo::isResizable() const { if constexpr (useProjectStorage()) { - if (isValid()) - return typeData().traits.isResizable; + if (!isValid()) + return FlagIs::False; - return FlagIs::False; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is resizable"_t, category(), keyValue("type id", m_typeId)}; + + auto isResizable = typeData().traits.isResizable; + + tracer.end(keyValue("is resizable", isResizable)); + + return isResizable; } return FlagIs::Set; @@ -1634,10 +1737,17 @@ FlagIs NodeMetaInfo::isResizable() const FlagIs NodeMetaInfo::hasFormEditorItem() const { if constexpr (useProjectStorage()) { - if (isValid()) - return typeData().traits.hasFormEditorItem; + if (!isValid()) + return FlagIs::False; - return FlagIs::False; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"has form editor item"_t, category(), keyValue("type id", m_typeId)}; + + auto hasFormEditorItem = typeData().traits.hasFormEditorItem; + + tracer.end(keyValue("has form editor item", hasFormEditorItem)); + + return hasFormEditorItem; } return FlagIs::Set; @@ -1646,10 +1756,17 @@ FlagIs NodeMetaInfo::hasFormEditorItem() const FlagIs NodeMetaInfo::isStackedContainer() const { if constexpr (useProjectStorage()) { - if (isValid()) - return typeData().traits.isStackedContainer; + if (!isValid()) + return FlagIs::False; - return FlagIs::False; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is stacked container"_t, category(), keyValue("type id", m_typeId)}; + + auto isStackedContainer = typeData().traits.isStackedContainer; + + tracer.end(keyValue("is stacked container", isStackedContainer)); + + return isStackedContainer; } return FlagIs::Set; @@ -1658,10 +1775,19 @@ FlagIs NodeMetaInfo::isStackedContainer() const FlagIs NodeMetaInfo::takesOverRenderingOfChildren() const { if constexpr (useProjectStorage()) { - if (isValid()) - return typeData().traits.takesOverRenderingOfChildren; + if (!isValid()) + return FlagIs::False; - return FlagIs::False; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"takes over rendering of children"_t, + category(), + keyValue("type id", m_typeId)}; + + auto takesOverRenderingOfChildren = typeData().traits.takesOverRenderingOfChildren; + + tracer.end(keyValue("takes over rendering of children", takesOverRenderingOfChildren)); + + return takesOverRenderingOfChildren; } return FlagIs::Set; @@ -1670,10 +1796,17 @@ FlagIs NodeMetaInfo::takesOverRenderingOfChildren() const FlagIs NodeMetaInfo::visibleInNavigator() const { if constexpr (useProjectStorage()) { - if (isValid()) - return typeData().traits.visibleInNavigator; + if (!isValid()) + return FlagIs::False; - return FlagIs::False; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"visible in navigator"_t, category(), keyValue("type id", m_typeId)}; + + auto visibleInNavigator = typeData().traits.visibleInNavigator; + + tracer.end(keyValue("visible in navigator", visibleInNavigator)); + + return visibleInNavigator; } return FlagIs::Set; @@ -1682,10 +1815,17 @@ FlagIs NodeMetaInfo::visibleInNavigator() const FlagIs NodeMetaInfo::visibleInLibrary() const { if constexpr (useProjectStorage()) { - if (isValid()) - return typeData().traits.visibleInLibrary; + if (!isValid()) + return FlagIs::False; - return FlagIs::False; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"visible in library"_t, category(), keyValue("type id", m_typeId)}; + + auto visibleInLibrary = typeData().traits.visibleInLibrary; + + tracer.end(keyValue("visible in library", visibleInLibrary)); + + return visibleInLibrary; } return FlagIs::Set; @@ -1697,6 +1837,12 @@ namespace { TypeId typeId, Utils::SmallStringView propertyName) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get combound property id"_t, + category(), + keyValue("type id", typeId), + keyValue("property name", propertyName)}; + auto begin = propertyName.begin(); const auto end = propertyName.end(); @@ -1712,11 +1858,17 @@ namespace { if (propertyId && found != end) { begin = std::next(found); - return projectStorage.propertyDeclarationId(propertyTypeId, {begin, end}); + auto id = projectStorage.propertyDeclarationId(propertyTypeId, {begin, end}); + + tracer.end(keyValue("property id", id)); + + return id; } } } + tracer.end(keyValue("property id", propertyId)); + return propertyId; } @@ -1724,10 +1876,24 @@ namespace { bool NodeMetaInfo::hasProperty(Utils::SmallStringView propertyName) const { - if constexpr (useProjectStorage()) - return isValid() && bool(propertyId(*m_projectStorage, m_typeId, propertyName)); - else + if constexpr (useProjectStorage()) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"has property"_t, + category(), + keyValue("type id", m_typeId), + keyValue("property name", propertyName)}; + + if (!isValid()) + return false; + + auto hasPropertyId = bool(propertyId(*m_projectStorage, m_typeId, propertyName)); + + tracer.end(keyValue("has property", hasPropertyId)); + + return hasPropertyId; + } else { return isValid() && m_privateData->properties().contains(QByteArrayView(propertyName)); + } } PropertyMetaInfos NodeMetaInfo::properties() const @@ -1736,12 +1902,14 @@ PropertyMetaInfos NodeMetaInfo::properties() const return {}; if constexpr (useProjectStorage()) { - if (isValid()) { - return Utils::transform( - m_projectStorage->propertyDeclarationIds(m_typeId), [&](auto id) { - return PropertyMetaInfo{id, m_projectStorage}; - }); - } + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get properties"_t, category(), keyValue("type id", m_typeId)}; + + return Utils::transform( + m_projectStorage->propertyDeclarationIds(m_typeId), [&](auto id) { + return PropertyMetaInfo{id, m_projectStorage}; + }); + } else { const auto &properties = m_privateData->properties(); @@ -1753,19 +1921,22 @@ PropertyMetaInfos NodeMetaInfo::properties() const return propertyMetaInfos; } - - return {}; } PropertyMetaInfos NodeMetaInfo::localProperties() const { + if (!isValid()) + return {}; + if constexpr (useProjectStorage()) { - if (isValid()) { - return Utils::transform( - m_projectStorage->localPropertyDeclarationIds(m_typeId), [&](auto id) { - return PropertyMetaInfo{id, m_projectStorage}; - }); - } + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get local properties"_t, category(), keyValue("type id", m_typeId)}; + + return Utils::transform( + m_projectStorage->localPropertyDeclarationIds(m_typeId), [&](auto id) { + return PropertyMetaInfo{id, m_projectStorage}; + }); + } else { const auto &properties = m_privateData->localProperties(); @@ -1777,71 +1948,82 @@ PropertyMetaInfos NodeMetaInfo::localProperties() const return propertyMetaInfos; } - - return {}; } PropertyMetaInfo NodeMetaInfo::property(const PropertyName &propertyName) const { + if (!isValid()) + return {}; + if constexpr (useProjectStorage()) { - if (isValid()) - return {propertyId(*m_projectStorage, m_typeId, propertyName), m_projectStorage}; + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get property"_t, + category(), + keyValue("type id", m_typeId), + keyValue("property name", propertyName)}; + + return {propertyId(*m_projectStorage, m_typeId, propertyName), m_projectStorage}; } else { if (hasProperty(propertyName)) { return PropertyMetaInfo{m_privateData, propertyName}; } + return {}; } - - return {}; } PropertyNameList NodeMetaInfo::signalNames() const { - if constexpr (useProjectStorage()) { - if (isValid()) { - return Utils::transform(m_projectStorage->signalDeclarationNames( - m_typeId), - [&](const auto &name) { - return name.toQByteArray(); - }); - } - } else { - if (isValid()) - return m_privateData->signalNames(); - } + if (!isValid()) + return {}; - return {}; + if constexpr (useProjectStorage()) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get signal names"_t, category(), keyValue("type id", m_typeId)}; + + return Utils::transform(m_projectStorage->signalDeclarationNames(m_typeId), + [&](const auto &name) { + return name.toQByteArray(); + }); + + } else { + return m_privateData->signalNames(); + } } PropertyNameList NodeMetaInfo::slotNames() const { - if constexpr (useProjectStorage()) { - if (isValid()) { - return Utils::transform(m_projectStorage->functionDeclarationNames( - m_typeId), - [&](const auto &name) { - return name.toQByteArray(); - }); - } - } else { - if (isValid()) - return m_privateData->slotNames(); - } + if (!isValid()) + return {}; - return {}; + if constexpr (useProjectStorage()) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get slot names"_t, category(), keyValue("type id", m_typeId)}; + return Utils::transform(m_projectStorage->functionDeclarationNames(m_typeId), + [&](const auto &name) { + return name.toQByteArray(); + }); + } else { + return m_privateData->slotNames(); + } } PropertyName NodeMetaInfo::defaultPropertyName() const { + if (!isValid()) + return {}; + if constexpr (useProjectStorage()) { - if (isValid()) { - if (auto name = m_projectStorage->propertyName(defaultPropertyDeclarationId())) { - return name->toQByteArray(); - } + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get default property name"_t, + category(), + keyValue("type id", m_typeId)}; + if (auto name = m_projectStorage->propertyName(defaultPropertyDeclarationId())) { + tracer.end(keyValue("default property name", name)); + return name->toQByteArray(); } + } else { - if (isValid()) - return m_privateData->defaultPropertyName(); + return m_privateData->defaultPropertyName(); } return {}; @@ -1849,88 +2031,128 @@ PropertyName NodeMetaInfo::defaultPropertyName() const PropertyMetaInfo NodeMetaInfo::defaultProperty() const { + if (!isValid()) + return {}; + if constexpr (useProjectStorage()) { - if (isValid()) { - return PropertyMetaInfo(defaultPropertyDeclarationId(), m_projectStorage); - } + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get default property"_t, category(), keyValue("type id", m_typeId)}; + + auto id = defaultPropertyDeclarationId(); + + tracer.end(keyValue("default property id", id)); + + return PropertyMetaInfo(id, m_projectStorage); } else { return property(defaultPropertyName()); } - - return {}; } bool NodeMetaInfo::hasDefaultProperty() const { - if constexpr (useProjectStorage()) - return isValid() && bool(defaultPropertyDeclarationId()); - else + if (!isValid()) + return false; + + if constexpr (useProjectStorage()) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"has default property"_t, category(), keyValue("type id", m_typeId)}; + auto hasDefaultProperty = bool(defaultPropertyDeclarationId()); + tracer.end(keyValue("has default property", hasDefaultProperty)); + + return hasDefaultProperty; + } else { return !defaultPropertyName().isEmpty(); + } } std::vector NodeMetaInfo::selfAndPrototypes() const { + if (!isValid()) + return {}; + if constexpr (useProjectStorage()) { - if (isValid()) { - return Utils::transform( - m_projectStorage->prototypeAndSelfIds(m_typeId), [&](TypeId typeId) { - return NodeMetaInfo{typeId, m_projectStorage}; - }); - } + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get self and prototypes"_t, + category(), + keyValue("type id", m_typeId)}; + + return Utils::transform(m_projectStorage->prototypeAndSelfIds(m_typeId), + [&](TypeId typeId) { + return NodeMetaInfo{typeId, m_projectStorage}; + }); } else { - if (isValid()) { - NodeMetaInfos hierarchy = {*this}; - Model *model = m_privateData->model(); - for (const TypeDescription &type : m_privateData->prototypes()) { - auto &last = hierarchy.emplace_back(model, - type.className.toUtf8(), - type.majorVersion, - type.minorVersion); - if (!last.isValid()) - hierarchy.pop_back(); - } - - return hierarchy; + NodeMetaInfos hierarchy = {*this}; + Model *model = m_privateData->model(); + for (const TypeDescription &type : m_privateData->prototypes()) { + auto &last = hierarchy.emplace_back(model, + type.className.toUtf8(), + type.majorVersion, + type.minorVersion); + if (!last.isValid()) + hierarchy.pop_back(); } - } - return {}; + return hierarchy; + } } NodeMetaInfos NodeMetaInfo::prototypes() const { + if (!isValid()) + return {}; + if constexpr (useProjectStorage()) { - if (isValid()) { - return Utils::transform( - m_projectStorage->prototypeIds(m_typeId), [&](TypeId typeId) { - return NodeMetaInfo{typeId, m_projectStorage}; - }); - } + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get prototypes"_t, category(), keyValue("type id", m_typeId)}; + return Utils::transform(m_projectStorage->prototypeIds(m_typeId), + [&](TypeId typeId) { + return NodeMetaInfo{typeId, m_projectStorage}; + }); + } else { - if (isValid()) { - NodeMetaInfos hierarchy; - Model *model = m_privateData->model(); - for (const TypeDescription &type : m_privateData->prototypes()) { - auto &last = hierarchy.emplace_back(model, - type.className.toUtf8(), - type.majorVersion, - type.minorVersion); - if (!last.isValid()) - hierarchy.pop_back(); - } - - return hierarchy; + NodeMetaInfos hierarchy; + Model *model = m_privateData->model(); + for (const TypeDescription &type : m_privateData->prototypes()) { + auto &last = hierarchy.emplace_back(model, + type.className.toUtf8(), + type.majorVersion, + type.minorVersion); + if (!last.isValid()) + hierarchy.pop_back(); } - } - return {}; + return hierarchy; + } } +namespace { +template +bool isBasedOnCommonType(NotNullPointer projectStorage, TypeId typeId) +{ + if (!typeId) + return false; + + auto base = projectStorage->commonTypeId(); + + return projectStorage->isBasedOn(typeId, base); +} +} // namespace + bool NodeMetaInfo::defaultPropertyIsComponent() const { - if (hasDefaultProperty()) - return defaultProperty().propertyType().isQmlComponent(); + if (!isValid()) + return false; - return false; + if (useProjectStorage()) { + auto id = defaultPropertyDeclarationId(); + auto propertyDeclaration = m_projectStorage->propertyDeclaration(id); + + using namespace Storage::Info; + return isBasedOnCommonType(m_projectStorage, propertyDeclaration->typeId); + } else { + if (hasDefaultProperty()) + return defaultProperty().propertyType().isQmlComponent(); + return false; + } } TypeName NodeMetaInfo::displayName() const @@ -1976,10 +2198,16 @@ int NodeMetaInfo::minorVersion() const Storage::Info::ExportedTypeNames NodeMetaInfo::allExportedTypeNames() const { + if (!isValid()) + return {}; + if constexpr (useProjectStorage()) { - if (isValid()) { - return m_projectStorage->exportedTypeNames(m_typeId); - } + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get all exported type names"_t, + category(), + keyValue("type id", m_typeId)}; + + return m_projectStorage->exportedTypeNames(m_typeId); } return {}; @@ -1987,10 +2215,17 @@ Storage::Info::ExportedTypeNames NodeMetaInfo::allExportedTypeNames() const Storage::Info::ExportedTypeNames NodeMetaInfo::exportedTypeNamesForSourceId(SourceId sourceId) const { + if (!isValid()) + return {}; + if constexpr (useProjectStorage()) { - if (isValid()) { - return m_projectStorage->exportedTypeNames(m_typeId, sourceId); - } + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get exported type names for source id"_t, + category(), + keyValue("type id", m_typeId), + keyValue("source id", sourceId)}; + + return m_projectStorage->exportedTypeNames(m_typeId, sourceId); } return {}; @@ -1998,9 +2233,18 @@ Storage::Info::ExportedTypeNames NodeMetaInfo::exportedTypeNamesForSourceId(Sour Storage::Info::TypeHints NodeMetaInfo::typeHints() const { + if (!isValid()) + return {}; + if constexpr (useProjectStorage()) { - if (isValid()) - return m_projectStorage->typeHints(m_typeId); + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get type hints"_t, category(), keyValue("type id", m_typeId)}; + + auto hints = m_projectStorage->typeHints(m_typeId); + + tracer.end(keyValue("type hints", hints)); + + return hints; } return {}; @@ -2008,9 +2252,18 @@ Storage::Info::TypeHints NodeMetaInfo::typeHints() const Utils::PathString NodeMetaInfo::iconPath() const { + if (!isValid()) + return {}; + if constexpr (useProjectStorage()) { - if (isValid()) - return m_projectStorage->typeIconPath(m_typeId); + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get icon path"_t, category(), keyValue("type id", m_typeId)}; + + auto iconPath = m_projectStorage->typeIconPath(m_typeId); + + tracer.end(keyValue("icon path", iconPath)); + + return iconPath; } return {}; @@ -2018,9 +2271,20 @@ Utils::PathString NodeMetaInfo::iconPath() const Storage::Info::ItemLibraryEntries NodeMetaInfo::itemLibrariesEntries() const { + if (!isValid()) + return {}; + if constexpr (useProjectStorage()) { - if (isValid()) - return m_projectStorage->itemLibraryEntries(m_typeId); + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get item library entries"_t, + category(), + keyValue("type id", m_typeId)}; + + auto entries = m_projectStorage->itemLibraryEntries(m_typeId); + + tracer.end(keyValue("item library entries", entries)); + + return entries; } return {}; @@ -2028,10 +2292,18 @@ Storage::Info::ItemLibraryEntries NodeMetaInfo::itemLibrariesEntries() const SourceId NodeMetaInfo::sourceId() const { + if (!isValid()) + return SourceId{}; + if constexpr (useProjectStorage()) { - if (isValid()) { - return typeData().sourceId; - } + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get source id"_t, category(), keyValue("type id", m_typeId)}; + + auto id = typeData().sourceId; + + tracer.end(keyValue("source id", id)); + + return id; } return SourceId{}; @@ -2064,18 +2336,31 @@ QString NodeMetaInfo::requiredImportString() const if (!isValid()) return {}; - Import imp = m_privateData->requiredImport(); - if (!imp.isEmpty()) - return imp.toImportString(); + if constexpr (!useProjectStorage()) { + Import imp = m_privateData->requiredImport(); + if (!imp.isEmpty()) + return imp.toImportString(); + } return {}; } SourceId NodeMetaInfo::propertyEditorPathId() const { + if (!isValid()) + return SourceId{}; + if (useProjectStorage()) { - if (isValid()) - return m_projectStorage->propertyEditorPathId(m_typeId); + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get property editor path id"_t, + category(), + keyValue("type id", m_typeId)}; + + auto id = m_projectStorage->propertyEditorPathId(m_typeId); + + tracer.end(keyValue("property editor path id", id)); + + return id; } return SourceId{}; @@ -2133,9 +2418,13 @@ bool NodeMetaInfo::isSubclassOf(const TypeName &type, int majorVersion, int mino bool NodeMetaInfo::isSuitableForMouseAreaFill() const { if constexpr (useProjectStorage()) { - if (!isValid()) { + if (!isValid()) return false; - } + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is suitable for mouse area fill"_t, + category(), + keyValue("type id", m_typeId)}; using namespace Storage::Info; auto itemId = m_projectStorage->commonTypeId(); @@ -2143,11 +2432,16 @@ bool NodeMetaInfo::isSuitableForMouseAreaFill() const auto controlsControlId = m_projectStorage->commonTypeId(); auto templatesControlId = m_projectStorage->commonTypeId(); - return m_projectStorage->isBasedOn(m_typeId, - itemId, - mouseAreaId, - controlsControlId, - templatesControlId); + auto isSuitableForMouseAreaFill = m_projectStorage->isBasedOn(m_typeId, + itemId, + mouseAreaId, + controlsControlId, + templatesControlId); + + tracer.end(keyValue("is suitable for mouse area fill", isSuitableForMouseAreaFill)); + + return isSuitableForMouseAreaFill; + } else { return isSubclassOf("QtQuick.Item") && !isSubclassOf("QtQuick.MouseArea") && !isSubclassOf("QtQuick.Controls.Control") @@ -2158,6 +2452,15 @@ bool NodeMetaInfo::isSuitableForMouseAreaFill() const bool NodeMetaInfo::isBasedOn(const NodeMetaInfo &metaInfo) const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is based on"_t, + category(), + keyValue("type id", m_typeId), + keyValue("meta info type id", metaInfo.m_typeId)}; + return m_projectStorage->isBasedOn(m_typeId, metaInfo.m_typeId); } else { if (!isValid()) @@ -2171,6 +2474,12 @@ bool NodeMetaInfo::isBasedOn(const NodeMetaInfo &metaInfo) const bool NodeMetaInfo::isBasedOn(const NodeMetaInfo &metaInfo1, const NodeMetaInfo &metaInfo2) const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is based on"_t, category(), keyValue("type id", m_typeId)}; + return m_projectStorage->isBasedOn(m_typeId, metaInfo1.m_typeId, metaInfo2.m_typeId); } else { if (!isValid()) @@ -2189,6 +2498,12 @@ bool NodeMetaInfo::isBasedOn(const NodeMetaInfo &metaInfo1, const NodeMetaInfo &metaInfo3) const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is based on"_t, category(), keyValue("type id", m_typeId)}; + return m_projectStorage->isBasedOn(m_typeId, metaInfo1.m_typeId, metaInfo2.m_typeId, @@ -2213,6 +2528,12 @@ bool NodeMetaInfo::isBasedOn(const NodeMetaInfo &metaInfo1, const NodeMetaInfo &metaInfo4) const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is based on"_t, category(), keyValue("type id", m_typeId)}; + return m_projectStorage->isBasedOn(m_typeId, metaInfo1.m_typeId, metaInfo2.m_typeId, @@ -2240,6 +2561,12 @@ bool NodeMetaInfo::isBasedOn(const NodeMetaInfo &metaInfo1, const NodeMetaInfo &metaInfo5) const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is based on"_t, category(), keyValue("type id", m_typeId)}; + return m_projectStorage->isBasedOn(m_typeId, metaInfo1.m_typeId, metaInfo2.m_typeId, @@ -2272,6 +2599,12 @@ bool NodeMetaInfo::isBasedOn(const NodeMetaInfo &metaInfo1, const NodeMetaInfo &metaInfo6) const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is based on"_t, category(), keyValue("type id", m_typeId)}; + return m_projectStorage->isBasedOn(m_typeId, metaInfo1.m_typeId, metaInfo2.m_typeId, @@ -2309,6 +2642,12 @@ bool NodeMetaInfo::isBasedOn(const NodeMetaInfo &metaInfo1, const NodeMetaInfo &metaInfo7) const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is based on"_t, category(), keyValue("type id", m_typeId)}; + return m_projectStorage->isBasedOn(m_typeId, metaInfo1.m_typeId, metaInfo2.m_typeId, @@ -2341,26 +2680,14 @@ bool NodeMetaInfo::isBasedOn(const NodeMetaInfo &metaInfo1, } } -namespace { -template -bool isBasedOnCommonType(NotNullPointer projectStorage, TypeId typeId) -{ - if (!typeId) { - return false; - } - - auto base = projectStorage->commonTypeId(); - - return projectStorage->isBasedOn(typeId, base); -} -} // namespace - bool NodeMetaInfo::isGraphicalItem() const { if constexpr (useProjectStorage()) { - if (!isValid()) { + if (!isValid()) return false; - } + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is graphical item"_t, category(), keyValue("type id", m_typeId)}; using namespace Storage::Info; auto itemId = m_projectStorage->commonTypeId(); @@ -2380,6 +2707,12 @@ bool NodeMetaInfo::isGraphicalItem() const bool NodeMetaInfo::isQtObject() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is Qt object"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -2390,6 +2723,14 @@ bool NodeMetaInfo::isQtObject() const bool NodeMetaInfo::isQtQmlConnections() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is Qt Qml connections"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -2400,9 +2741,11 @@ bool NodeMetaInfo::isQtQmlConnections() const bool NodeMetaInfo::isLayoutable() const { if constexpr (useProjectStorage()) { - if (!isValid()) { + if (!isValid()) return false; - } + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is layoutable"_t, category(), keyValue("type id", m_typeId)}; using namespace Storage::Info; auto positionerId = m_projectStorage->commonTypeId(); @@ -2421,6 +2764,14 @@ bool NodeMetaInfo::isLayoutable() const bool NodeMetaInfo::isQtQuickLayoutsLayout() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.Layouts.Layout"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -2431,9 +2782,11 @@ bool NodeMetaInfo::isQtQuickLayoutsLayout() const bool NodeMetaInfo::isView() const { if constexpr (useProjectStorage()) { - if (!isValid()) { + if (!isValid()) return false; - } + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is view"_t, category(), keyValue("type id", m_typeId)}; using namespace Storage::Info; auto listViewId = m_projectStorage->commonTypeId(); @@ -2450,7 +2803,13 @@ bool NodeMetaInfo::isView() const bool NodeMetaInfo::usesCustomParser() const { if constexpr (useProjectStorage()) { - return isValid() && typeData().traits.usesCustomParser; + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"uses custom parser"_t, category(), keyValue("type id", m_typeId)}; + + return typeData().traits.usesCustomParser; } else { if (!isValid()) return false; @@ -2476,8 +2835,14 @@ bool isTypeId(TypeId typeId, TypeIds... otherTypeIds) bool NodeMetaInfo::isVector2D() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is vector2d"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; - return isValid() && isTypeId(m_typeId, m_projectStorage->commonTypeId()); + return isTypeId(m_typeId, m_projectStorage->commonTypeId()); } else { if (!m_privateData) return false; @@ -2491,8 +2856,14 @@ bool NodeMetaInfo::isVector2D() const bool NodeMetaInfo::isVector3D() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is vector3d"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; - return isValid() && isTypeId(m_typeId, m_projectStorage->commonTypeId()); + return isTypeId(m_typeId, m_projectStorage->commonTypeId()); } else { if (!m_privateData) return false; @@ -2506,8 +2877,14 @@ bool NodeMetaInfo::isVector3D() const bool NodeMetaInfo::isVector4D() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is vector4d"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; - return isValid() && isTypeId(m_typeId, m_projectStorage->commonTypeId()); + return isTypeId(m_typeId, m_projectStorage->commonTypeId()); } else { if (!m_privateData) return false; @@ -2521,6 +2898,14 @@ bool NodeMetaInfo::isVector4D() const bool NodeMetaInfo::isQtQuickPropertyChanges() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.PropertyChanges"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); @@ -2532,6 +2917,14 @@ bool NodeMetaInfo::isQtQuickPropertyChanges() const bool NodeMetaInfo::isQtSafeRendererSafeRendererPicture() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is Qt.SafeRenderer.SafeRendererPicture"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -2542,6 +2935,14 @@ bool NodeMetaInfo::isQtSafeRendererSafeRendererPicture() const bool NodeMetaInfo::isQtSafeRendererSafePicture() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is Qt.SafeRenderer.SafePicture"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -2552,6 +2953,14 @@ bool NodeMetaInfo::isQtSafeRendererSafePicture() const bool NodeMetaInfo::isQtQuickTimelineKeyframe() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.Timeline.Keyframe"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); @@ -2563,6 +2972,14 @@ bool NodeMetaInfo::isQtQuickTimelineKeyframe() const bool NodeMetaInfo::isQtQuickTimelineTimelineAnimation() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.Timeline.TimelineAnimation"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -2573,6 +2990,14 @@ bool NodeMetaInfo::isQtQuickTimelineTimelineAnimation() const bool NodeMetaInfo::isQtQuickTimelineTimeline() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.Timeline.Timeline"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -2583,6 +3008,14 @@ bool NodeMetaInfo::isQtQuickTimelineTimeline() const bool NodeMetaInfo::isQtQuickTimelineKeyframeGroup() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.Timeline.KeyframeGroup"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -2593,9 +3026,11 @@ bool NodeMetaInfo::isQtQuickTimelineKeyframeGroup() const bool NodeMetaInfo::isListOrGridView() const { if constexpr (useProjectStorage()) { - if (!isValid()) { + if (!isValid()) return false; - } + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is list or grid view"_t, category(), keyValue("type id", m_typeId)}; using namespace Storage::Info; auto listViewId = m_projectStorage->commonTypeId(); @@ -2609,9 +3044,11 @@ bool NodeMetaInfo::isListOrGridView() const bool NodeMetaInfo::isNumber() const { if constexpr (useProjectStorage()) { - if (!isValid()) { + if (!isValid()) return false; - } + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is number"_t, category(), keyValue("type id", m_typeId)}; using namespace Storage::Info; auto intId = m_projectStorage->builtinTypeId(); @@ -2632,6 +3069,14 @@ bool NodeMetaInfo::isNumber() const bool NodeMetaInfo::isQtQuickExtrasPicture() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.Extras.Picture"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -2642,6 +3087,12 @@ bool NodeMetaInfo::isQtQuickExtrasPicture() const bool NodeMetaInfo::isQtQuickImage() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.Image"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); @@ -2653,6 +3104,14 @@ bool NodeMetaInfo::isQtQuickImage() const bool NodeMetaInfo::isQtQuickBorderImage() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.BorderImage"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); @@ -2664,7 +3123,13 @@ bool NodeMetaInfo::isQtQuickBorderImage() const bool NodeMetaInfo::isAlias() const { if constexpr (useProjectStorage()) { - return false; // there is no type alias + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is alias"_t, category(), keyValue("type id", m_typeId)}; + + return false; // all types are already resolved } else { return isValid() && m_privateData->qualfiedTypeName() == "alias"; } @@ -2673,6 +3138,14 @@ bool NodeMetaInfo::isAlias() const bool NodeMetaInfo::isQtQuickPositioner() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.Positioner"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); @@ -2684,6 +3157,14 @@ bool NodeMetaInfo::isQtQuickPositioner() const bool NodeMetaInfo::isQtQuickPropertyAnimation() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.PropertyAnimation"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -2694,6 +3175,12 @@ bool NodeMetaInfo::isQtQuickPropertyAnimation() const bool NodeMetaInfo::isQtQuickRepeater() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.Repeater"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -2704,6 +3191,14 @@ bool NodeMetaInfo::isQtQuickRepeater() const bool NodeMetaInfo::isQtQuickControlsTabBar() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.Controls.TabBar"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -2714,6 +3209,14 @@ bool NodeMetaInfo::isQtQuickControlsTabBar() const bool NodeMetaInfo::isQtQuickControlsSwipeView() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.Controls.SwipeView"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -2724,6 +3227,12 @@ bool NodeMetaInfo::isQtQuickControlsSwipeView() const bool NodeMetaInfo::isQtQuick3DCamera() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.Camera"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -2734,6 +3243,14 @@ bool NodeMetaInfo::isQtQuick3DCamera() const bool NodeMetaInfo::isQtQuick3DBakedLightmap() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.BakedLightmap"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -2744,6 +3261,12 @@ bool NodeMetaInfo::isQtQuick3DBakedLightmap() const bool NodeMetaInfo::isQtQuick3DBuffer() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.Buffer"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -2754,6 +3277,14 @@ bool NodeMetaInfo::isQtQuick3DBuffer() const bool NodeMetaInfo::isQtQuick3DInstanceListEntry() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.InstanceListEntry"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -2764,6 +3295,12 @@ bool NodeMetaInfo::isQtQuick3DInstanceListEntry() const bool NodeMetaInfo::isQtQuick3DLight() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.Light"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -2774,6 +3311,14 @@ bool NodeMetaInfo::isQtQuick3DLight() const bool NodeMetaInfo::isQtQmlModelsListElement() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQml.Models.ListElement"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -2784,6 +3329,12 @@ bool NodeMetaInfo::isQtQmlModelsListElement() const bool NodeMetaInfo::isQtQuickListModel() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.ListModel"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -2794,6 +3345,12 @@ bool NodeMetaInfo::isQtQuickListModel() const bool NodeMetaInfo::isQtQuickListView() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.ListView"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -2801,9 +3358,33 @@ bool NodeMetaInfo::isQtQuickListView() const } } +bool QmlDesigner::NodeMetaInfo::isQtQuickGridView() const +{ + if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.GridView"_t, category(), keyValue("type id", m_typeId)}; + + using namespace Storage::Info; + return isBasedOnCommonType(m_projectStorage, m_typeId); + } else { + return isValid() && (isSubclassOf("QtQuick.GridView")); + } +} + bool NodeMetaInfo::isQtQuick3DInstanceList() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.InstanceList"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -2814,6 +3395,14 @@ bool NodeMetaInfo::isQtQuick3DInstanceList() const bool NodeMetaInfo::isQtQuick3DParticles3DParticle3D() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.Particles3D.Particle3D"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -2824,6 +3413,14 @@ bool NodeMetaInfo::isQtQuick3DParticles3DParticle3D() const bool NodeMetaInfo::isQtQuick3DParticles3DParticleEmitter3D() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.Particles3D.ParticleEmitter3D"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); @@ -2835,6 +3432,14 @@ bool NodeMetaInfo::isQtQuick3DParticles3DParticleEmitter3D() const bool NodeMetaInfo::isQtQuick3DParticles3DAttractor3D() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.Particles3D.Attractor3D"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -2845,8 +3450,16 @@ bool NodeMetaInfo::isQtQuick3DParticles3DAttractor3D() const bool NodeMetaInfo::isQtQuick3DParticlesAbstractShape() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.Particles3D.AbstractShape"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; - return isBasedOnCommonType( + return isBasedOnCommonType( m_projectStorage, m_typeId); } else { return isValid() && isSubclassOf("QQuick3DParticleAbstractShape"); @@ -2856,6 +3469,12 @@ bool NodeMetaInfo::isQtQuick3DParticlesAbstractShape() const bool NodeMetaInfo::isQtQuickItem() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.Item"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -2866,6 +3485,12 @@ bool NodeMetaInfo::isQtQuickItem() const bool NodeMetaInfo::isQtQuickPath() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.Path"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -2876,6 +3501,14 @@ bool NodeMetaInfo::isQtQuickPath() const bool NodeMetaInfo::isQtQuickPauseAnimation() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.PauseAnimation"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -2886,6 +3519,14 @@ bool NodeMetaInfo::isQtQuickPauseAnimation() const bool NodeMetaInfo::isQtQuickTransition() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.Transition"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -2896,6 +3537,14 @@ bool NodeMetaInfo::isQtQuickTransition() const bool NodeMetaInfo::isQtQuickWindowWindow() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.Window.Window"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -2906,6 +3555,12 @@ bool NodeMetaInfo::isQtQuickWindowWindow() const bool NodeMetaInfo::isQtQuickLoader() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.Loader"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -2916,6 +3571,12 @@ bool NodeMetaInfo::isQtQuickLoader() const bool NodeMetaInfo::isQtQuickState() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.State"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -2926,9 +3587,17 @@ bool NodeMetaInfo::isQtQuickState() const bool NodeMetaInfo::isQtQuickStateOperation() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.StateOperation"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; - return isBasedOnCommonType(m_projectStorage, - m_typeId); + return isBasedOnCommonType(m_projectStorage, + m_typeId); } else { return isValid() && isSubclassOf(".QQuickStateOperation"); } @@ -2937,6 +3606,12 @@ bool NodeMetaInfo::isQtQuickStateOperation() const bool NodeMetaInfo::isQtQuickText() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.Text"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -2947,6 +3622,14 @@ bool NodeMetaInfo::isQtQuickText() const bool NodeMetaInfo::isQtMultimediaSoundEffect() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtMultimedia.SoundEffect"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -2957,9 +3640,11 @@ bool NodeMetaInfo::isQtMultimediaSoundEffect() const bool NodeMetaInfo::isFlowViewItem() const { if constexpr (useProjectStorage()) { - if (!isValid()) { + if (!isValid()) return false; - } + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is FlowView.ViewItem"_t, category(), keyValue("type id", m_typeId)}; using namespace Storage::Info; auto flowItemId = m_projectStorage->commonTypeId(); @@ -2976,6 +3661,12 @@ bool NodeMetaInfo::isFlowViewItem() const bool NodeMetaInfo::isFlowViewFlowItem() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is FlowView.FlowItem"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -2986,6 +3677,12 @@ bool NodeMetaInfo::isFlowViewFlowItem() const bool NodeMetaInfo::isFlowViewFlowView() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is FlowView.FlowView"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -3006,6 +3703,14 @@ bool NodeMetaInfo::isFlowViewFlowActionArea() const bool NodeMetaInfo::isFlowViewFlowTransition() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is FlowView.FlowTransition"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -3016,6 +3721,14 @@ bool NodeMetaInfo::isFlowViewFlowTransition() const bool NodeMetaInfo::isFlowViewFlowDecision() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is FlowView.FlowDecision"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -3026,6 +3739,14 @@ bool NodeMetaInfo::isFlowViewFlowDecision() const bool NodeMetaInfo::isFlowViewFlowWildcard() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is FlowView.FlowWildcard"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -3036,6 +3757,14 @@ bool NodeMetaInfo::isFlowViewFlowWildcard() const bool NodeMetaInfo::isQtQuickStudioComponentsGroupItem() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.Studio.Components.GroupItem"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -3046,6 +3775,14 @@ bool NodeMetaInfo::isQtQuickStudioComponentsGroupItem() const bool NodeMetaInfo::isQtQuickStudioUtilsJsonListModel() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick.Studio.Utils.JsonListModel"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); @@ -3057,6 +3794,12 @@ bool NodeMetaInfo::isQtQuickStudioUtilsJsonListModel() const bool NodeMetaInfo::isQmlComponent() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QML.Component"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -3072,6 +3815,12 @@ bool NodeMetaInfo::isQmlComponent() const bool NodeMetaInfo::isFont() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is font"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isValid() && isTypeId(m_typeId, m_projectStorage->commonTypeId()); } else { @@ -3082,6 +3831,12 @@ bool NodeMetaInfo::isFont() const bool NodeMetaInfo::isColor() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is color"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isValid() && isTypeId(m_typeId, m_projectStorage->builtinTypeId()); } else { @@ -3097,6 +3852,12 @@ bool NodeMetaInfo::isColor() const bool NodeMetaInfo::isBool() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is bool"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isValid() && isTypeId(m_typeId, m_projectStorage->builtinTypeId()); } else { @@ -3112,6 +3873,12 @@ bool NodeMetaInfo::isBool() const bool NodeMetaInfo::isInteger() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is integer"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isValid() && isTypeId(m_typeId, m_projectStorage->builtinTypeId()); } else { @@ -3127,9 +3894,11 @@ bool NodeMetaInfo::isInteger() const bool NodeMetaInfo::isFloat() const { if constexpr (useProjectStorage()) { - if (!isValid()) { + if (!isValid()) return false; - } + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is float"_t, category(), keyValue("type id", m_typeId)}; using namespace Storage::Info; auto floatId = m_projectStorage->builtinTypeId(); @@ -3149,6 +3918,12 @@ bool NodeMetaInfo::isFloat() const bool NodeMetaInfo::isVariant() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is variant"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isValid() && isTypeId(m_typeId, m_projectStorage->builtinTypeId()); } else { @@ -3164,6 +3939,12 @@ bool NodeMetaInfo::isVariant() const bool NodeMetaInfo::isString() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is string"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isValid() && isTypeId(m_typeId, m_projectStorage->builtinTypeId()); } else { @@ -3179,6 +3960,12 @@ bool NodeMetaInfo::isString() const bool NodeMetaInfo::isUrl() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is url"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isValid() && isTypeId(m_typeId, m_projectStorage->builtinTypeId()); } else { @@ -3194,6 +3981,12 @@ bool NodeMetaInfo::isUrl() const bool NodeMetaInfo::isQtQuick3DTexture() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.Texture"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -3205,6 +3998,12 @@ bool NodeMetaInfo::isQtQuick3DTexture() const bool NodeMetaInfo::isQtQuick3DShader() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.Shader"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -3215,6 +4014,12 @@ bool NodeMetaInfo::isQtQuick3DShader() const bool NodeMetaInfo::isQtQuick3DPass() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.Pass"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -3225,6 +4030,12 @@ bool NodeMetaInfo::isQtQuick3DPass() const bool NodeMetaInfo::isQtQuick3DCommand() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.Command"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -3235,6 +4046,14 @@ bool NodeMetaInfo::isQtQuick3DCommand() const bool NodeMetaInfo::isQtQuick3DDefaultMaterial() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.DefaultMaterial"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -3255,6 +4074,12 @@ bool NodeMetaInfo::isQtQuick3DMaterial() const bool NodeMetaInfo::isQtQuick3DModel() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.Model"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -3265,6 +4090,12 @@ bool NodeMetaInfo::isQtQuick3DModel() const bool NodeMetaInfo::isQtQuick3DNode() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.Node"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -3275,6 +4106,14 @@ bool NodeMetaInfo::isQtQuick3DNode() const bool NodeMetaInfo::isQtQuick3DParticles3DAffector3D() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.Particles3D.Affector3D"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -3285,6 +4124,12 @@ bool NodeMetaInfo::isQtQuick3DParticles3DAffector3D() const bool NodeMetaInfo::isQtQuick3DView3D() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.View3D"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -3295,6 +4140,14 @@ bool NodeMetaInfo::isQtQuick3DView3D() const bool NodeMetaInfo::isQtQuick3DPrincipledMaterial() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.PrincipledMaterial"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -3305,6 +4158,14 @@ bool NodeMetaInfo::isQtQuick3DPrincipledMaterial() const bool NodeMetaInfo::isQtQuick3DSpecularGlossyMaterial() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.SpecularGlossyMaterial"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -3315,6 +4176,14 @@ bool NodeMetaInfo::isQtQuick3DSpecularGlossyMaterial() const bool NodeMetaInfo::isQtQuick3DParticles3DSpriteParticle3D() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.Particles3D.SpriteParticle3D"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); @@ -3326,6 +4195,14 @@ bool NodeMetaInfo::isQtQuick3DParticles3DSpriteParticle3D() const bool NodeMetaInfo::isQtQuick3DTextureInput() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.TextureInput"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -3336,6 +4213,14 @@ bool NodeMetaInfo::isQtQuick3DTextureInput() const bool NodeMetaInfo::isQtQuick3DCubeMapTexture() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.CubeMapTexture"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -3348,6 +4233,14 @@ bool NodeMetaInfo::isQtQuick3DCubeMapTexture() const bool NodeMetaInfo::isQtQuick3DSceneEnvironment() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.SceneEnvironment"_t, + category(), + keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -3358,6 +4251,12 @@ bool NodeMetaInfo::isQtQuick3DSceneEnvironment() const bool NodeMetaInfo::isQtQuick3DEffect() const { if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is QtQuick3D.Effect"_t, category(), keyValue("type id", m_typeId)}; + using namespace Storage::Info; return isBasedOnCommonType(m_projectStorage, m_typeId); } else { @@ -3367,8 +4266,15 @@ bool NodeMetaInfo::isQtQuick3DEffect() const bool NodeMetaInfo::isEnumeration() const { - if constexpr (useProjectStorage()) - return isValid() && typeData().traits.isEnum; + if constexpr (useProjectStorage()) { + if (!isValid()) + return false; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is enumeration"_t, category(), keyValue("type id", m_typeId)}; + + return typeData().traits.isEnum; + } return false; } @@ -3393,8 +4299,15 @@ PropertyMetaInfo::~PropertyMetaInfo() = default; NodeMetaInfo PropertyMetaInfo::propertyType() const { if constexpr (useProjectStorage()) { - if (isValid()) - return {propertyData().propertyTypeId, m_projectStorage}; + if (!isValid()) + return {}; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get property type"_t, + category(), + keyValue("property declaration id", m_id)}; + + return {propertyData().propertyTypeId, m_projectStorage}; } else { if (isValid()) return NodeMetaInfo{nodeMetaInfoPrivateData()->model(), @@ -3409,8 +4322,15 @@ NodeMetaInfo PropertyMetaInfo::propertyType() const NodeMetaInfo PropertyMetaInfo::type() const { if constexpr (useProjectStorage()) { - if (isValid()) - return NodeMetaInfo(propertyData().typeId, m_projectStorage); + if (!isValid()) + return {}; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get property owner type "_t, + category(), + keyValue("property declaration id", m_id)}; + + return NodeMetaInfo(propertyData().typeId, m_projectStorage); } return {}; @@ -3418,59 +4338,121 @@ NodeMetaInfo PropertyMetaInfo::type() const PropertyName PropertyMetaInfo::name() const { - if (isValid()) { - if constexpr (useProjectStorage()) - return PropertyName(Utils::SmallStringView(propertyData().name)); - else - return propertyName(); - } + if (!isValid()) + return {}; - return {}; + if constexpr (useProjectStorage()) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get property name"_t, + category(), + keyValue("property declaration id", m_id)}; + + return PropertyName(Utils::SmallStringView(propertyData().name)); + } else { + return propertyName(); + } } bool PropertyMetaInfo::isWritable() const { - if constexpr (useProjectStorage()) - return isValid() && !(propertyData().traits & Storage::PropertyDeclarationTraits::IsReadOnly); - else + if constexpr (useProjectStorage()) { + if (!isValid()) + return {}; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is property writable"_t, + category(), + keyValue("property declaration id", m_id)}; + + return !(propertyData().traits & Storage::PropertyDeclarationTraits::IsReadOnly); + } else { return isValid() && nodeMetaInfoPrivateData()->isPropertyWritable(propertyName()); + } } bool PropertyMetaInfo::isReadOnly() const { - return !isWritable(); + if constexpr (useProjectStorage()) { + if (!isValid()) + return {}; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is property read only"_t, + category(), + keyValue("property declaration id", m_id)}; + + return propertyData().traits & Storage::PropertyDeclarationTraits::IsReadOnly; + } else { + return !isWritable(); + } } bool PropertyMetaInfo::isListProperty() const { - if constexpr (useProjectStorage()) - return isValid() && propertyData().traits & Storage::PropertyDeclarationTraits::IsList; - else + if constexpr (useProjectStorage()) { + if (!isValid()) + return {}; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is list property"_t, + category(), + keyValue("property declaration id", m_id)}; + + return propertyData().traits & Storage::PropertyDeclarationTraits::IsList; + } else { return isValid() && nodeMetaInfoPrivateData()->isPropertyList(propertyName()); + } } bool PropertyMetaInfo::isEnumType() const { - if constexpr (useProjectStorage()) + if constexpr (useProjectStorage()) { + if (!isValid()) + return {}; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is enum type"_t, + category(), + keyValue("property has enumeration type", m_id)}; + return propertyType().isEnumeration(); - else + } else { return isValid() && nodeMetaInfoPrivateData()->isPropertyEnum(propertyName()); + } } bool PropertyMetaInfo::isPrivate() const { - if constexpr (useProjectStorage()) + if constexpr (useProjectStorage()) { + if (!isValid()) + return {}; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is private property"_t, + category(), + keyValue("property declaration id", m_id)}; + return isValid() && propertyData().name.startsWith("__"); - else + } else { return isValid() && propertyName().startsWith("__"); + } } bool PropertyMetaInfo::isPointer() const { - if constexpr (useProjectStorage()) + if constexpr (useProjectStorage()) { + if (!isValid()) + return {}; + + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"is pointer property"_t, + category(), + keyValue("property declaration id", m_id)}; + return isValid() && (propertyData().traits & Storage::PropertyDeclarationTraits::IsPointer); - else + } else { return isValid() && nodeMetaInfoPrivateData()->isPropertyPointer(propertyName()); + } } namespace { @@ -3487,6 +4469,11 @@ QVariant PropertyMetaInfo::castedValue(const QVariant &value) const return {}; if constexpr (!useProjectStorage()) { + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"cast value"_t, + category(), + keyValue("property declaration id", m_id)}; + const QVariant variant = value; QVariant copyVariant = variant; const TypeName &typeName = propertyTypeName(); diff --git a/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp b/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp index 16d9217f6a6..29a093f199e 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp @@ -345,11 +345,8 @@ void SubComponentManager::unregisterQmlFile(const QFileInfo &fileInfo, const QSt void SubComponentManager::registerQmlFile(const QFileInfo &fileInfo, const QString &qualifier, bool addToLibrary) { - if (!addToLibrary || !model() - || m_componentUtils.isImport3dPath(fileInfo.path()) - || m_componentUtils.isComposedEffectPath(fileInfo.path())) { + if (!addToLibrary || !model() || m_componentUtils.isGeneratedPath(fileInfo.path())) return; - } QString componentName = fileInfo.baseName(); const QString baseComponentName = componentName; diff --git a/src/plugins/qmldesigner/designercore/model/model.cpp b/src/plugins/qmldesigner/designercore/model/model.cpp index 4f0bfba1ced..d4eea263785 100644 --- a/src/plugins/qmldesigner/designercore/model/model.cpp +++ b/src/plugins/qmldesigner/designercore/model/model.cpp @@ -9,7 +9,6 @@ #include "../projectstorage/sourcepath.h" #include "../projectstorage/sourcepathcache.h" #include "abstractview.h" -#include "auxiliarydataproperties.h" #include "internalbindingproperty.h" #include "internalnodeabstractproperty.h" #include "internalnodelistproperty.h" @@ -33,6 +32,8 @@ #include "signalhandlerproperty.h" #include "variantproperty.h" +#include + #include #include @@ -46,8 +47,6 @@ #include #include -#include - /*! \defgroup CoreModel */ @@ -170,10 +169,10 @@ Storage::Imports createStorageImports(const Imports &imports, SourceId fileId) { return Utils::transform(imports, [&](const Import &import) { - return Storage::Import{projectStorage.moduleId(Utils::SmallString{import.url()}), - import.majorVersion(), - import.minorVersion(), - fileId}; + using Storage::ModuleKind; + auto moduleKind = import.isLibraryImport() ? ModuleKind::QmlLibrary : ModuleKind::PathLibrary; + auto moduleId = projectStorage.moduleId(Utils::SmallString{import.url()}, moduleKind); + return Storage::Import{moduleId, import.majorVersion(), import.minorVersion(), fileId}; }); } @@ -244,7 +243,7 @@ void ModelPrivate::notifyUsedImportsChanged(const Imports &usedImports) } } -QUrl ModelPrivate::fileUrl() const +const QUrl &ModelPrivate::fileUrl() const { return m_fileUrl; } @@ -390,7 +389,11 @@ ImportedTypeNameId ModelPrivate::importedTypeNameId(Utils::SmallStringView typeN return import.alias() == aliasName; }); if (found != m_imports.end()) { - ModuleId moduleId = projectStorage->moduleId(Utils::PathString{found->url()}); + using Storage::ModuleKind; + auto moduleKind = found->isLibraryImport() ? ModuleKind::QmlLibrary + : ModuleKind::PathLibrary; + ModuleId moduleId = projectStorage->moduleId(Utils::PathString{found->url()}, + moduleKind); ImportId importId = projectStorage->importId( Storage::Import{moduleId, found->majorVersion(), found->minorVersion(), m_sourceId}); return projectStorage->importedTypeNameId(importId, shortTypeName); @@ -1860,90 +1863,18 @@ bool Model::hasImport(const QString &importUrl) const }); } -static QString firstCharToLower(const QString &string) +QString Model::generateNewId(const QString &prefixName, const QString &fallbackPrefix) const { - QString resultString = string; + QString newId = prefixName; - if (!resultString.isEmpty()) - resultString[0] = resultString.at(0).toLower(); + if (newId.isEmpty()) + newId = fallbackPrefix; - return resultString; -} - -QString Model::generateNewId(const QString &prefixName, - const QString &fallbackPrefix, - std::optional> isDuplicate) const -{ - // First try just the prefixName without number as postfix, then continue with 2 and further - // as postfix until id does not already exist. - // Properties of the root node are not allowed for ids, because they are available in the - // complete context without qualification. - - int counter = 0; - - static const QRegularExpression nonWordCharsRegex("\\W"); - QString newBaseId = firstCharToLower(prefixName); - newBaseId.remove(nonWordCharsRegex); - - if (!newBaseId.isEmpty()) { - QChar firstChar = newBaseId.at(0); - if (firstChar.isDigit()) - newBaseId.prepend('_'); - } else { - newBaseId = fallbackPrefix; - } - - QString newId = newBaseId; - - if (!isDuplicate.has_value()) - isDuplicate = std::bind(&Model::hasId, this, std::placeholders::_1); - - while (!ModelNode::isValidId(newId) || isDuplicate.value()(newId) - || d->rootNode()->property(newId.toUtf8())) { - ++counter; - newId = QStringView(u"%1%2").arg(firstCharToLower(newBaseId)).arg(counter); - } - - return newId; -} - -// Generate a unique camelCase id from a name -// note: this methods does the same as generateNewId(). The 2 methods should be merged into one -QString Model::generateIdFromName(const QString &name, const QString &fallbackId) const -{ - QString newId; - if (name.isEmpty()) { - newId = fallbackId; - } else { - // convert to camel case - QStringList nameWords = name.split(" "); - nameWords[0] = nameWords[0].at(0).toLower() + nameWords[0].mid(1); - for (int i = 1; i < nameWords.size(); ++i) - nameWords[i] = nameWords[i].at(0).toUpper() + nameWords[i].mid(1); - newId = nameWords.join(""); - - // if id starts with a number prepend an underscore - if (newId.at(0).isDigit()) - newId.prepend('_'); - } - - // If the new id is not valid (e.g. qml keyword match), try fixing it by prepending underscore - if (!ModelNode::isValidId(newId)) - newId.prepend("_"); - - QRegularExpression rgx("\\d+$"); // matches a number at the end of a string - while (hasId(newId)) { // id exists - QRegularExpressionMatch match = rgx.match(newId); - if (match.hasMatch()) { // ends with a number, increment it - QString numStr = match.captured(); - int num = numStr.toInt() + 1; - newId = newId.mid(0, match.capturedStart()) + QString::number(num); - } else { - newId.append('1'); - } - } - - return newId; + return UniqueName::generateId(prefixName, [&] (const QString &id) { + // Properties of the root node are not allowed for ids, because they are available in the + // complete context without qualification. + return hasId(id) || d->rootNode()->property(id.toUtf8()); + }); } void Model::startDrag(QMimeData *mimeData, const QPixmap &icon) @@ -2115,7 +2046,7 @@ void Model::clearMetaInfoCache() \brief Returns the URL against which relative URLs within the model should be resolved. \return The base URL. */ -QUrl Model::fileUrl() const +const QUrl &Model::fileUrl() const { return d->fileUrl(); } @@ -2556,8 +2487,7 @@ QList Model::itemLibraryEntries() const { #ifdef QDS_USE_PROJECTSTORAGE using namespace Storage::Info; - return toItemLibraryEntries(d->projectStorage->itemLibraryEntries(d->m_sourceId), - *d->projectStorage); + return toItemLibraryEntries(d->projectStorage->itemLibraryEntries(d->m_sourceId)); #else return d->metaInfo().itemLibraryInfo()->entries(); #endif @@ -2623,11 +2553,10 @@ MetaInfo Model::metaInfo() } #endif -Module Model::module(Utils::SmallStringView moduleName) +Module Model::module(Utils::SmallStringView moduleName, Storage::ModuleKind moduleKind) { - if constexpr (useProjectStorage()) { - return Module(d->projectStorage->moduleId(moduleName), d->projectStorage); - } + if constexpr (useProjectStorage()) + return Module(d->projectStorage->moduleId(moduleName, moduleKind), d->projectStorage); return {}; } diff --git a/src/plugins/qmldesigner/designercore/model/model_p.h b/src/plugins/qmldesigner/designercore/model/model_p.h index a3e972f329f..cb082fd1d70 100644 --- a/src/plugins/qmldesigner/designercore/model/model_p.h +++ b/src/plugins/qmldesigner/designercore/model/model_p.h @@ -122,7 +122,7 @@ public: ModelPrivate(const ModelPrivate &) = delete; ModelPrivate &operator=(const ModelPrivate &) = delete; - QUrl fileUrl() const; + const QUrl &fileUrl() const; void setFileUrl(const QUrl &url); InternalNodePointer createNode(const TypeName &typeName, diff --git a/src/plugins/qmldesigner/designercore/model/qmlvisualnode.cpp b/src/plugins/qmldesigner/designercore/model/qmlvisualnode.cpp index c84f2342576..726d3d52af4 100644 --- a/src/plugins/qmldesigner/designercore/model/qmlvisualnode.cpp +++ b/src/plugins/qmldesigner/designercore/model/qmlvisualnode.cpp @@ -24,6 +24,8 @@ #include #include +#include + namespace QmlDesigner { static char imagePlaceHolder[] = "qrc:/qtquickplugin/images/template_image.png"; @@ -182,8 +184,8 @@ void QmlVisualNode::scatter(const ModelNode &targetNode, const std::optionaltranslate(QVector3D(offsetValue, offsetValue, offsetValue)); } else { // scatter in range const double scatterRange = 20.; @@ -250,8 +252,7 @@ QmlObjectNode QmlVisualNode::createQmlObjectNode(AbstractView *view, NodeAbstractProperty parentProperty = parentQmlItemNode.defaultNodeAbstractProperty(); - - NodeHints hints = NodeHints::fromItemLibraryEntry(itemLibraryEntry); + NodeHints hints = NodeHints::fromItemLibraryEntry(itemLibraryEntry, view->model()); const PropertyName forceNonDefaultProperty = hints.forceNonDefaultProperty().toUtf8(); QmlObjectNode newNode = QmlItemNode::createQmlObjectNode(view, @@ -289,17 +290,17 @@ static QmlObjectNode createQmlObjectNodeFromSource(AbstractView *view, textEdit.setPlainText(source); NotIndentingTextEditModifier modifier(&textEdit); - QScopedPointer rewriterView( - new RewriterView(view->externalDependencies(), RewriterView::Amend)); + std::unique_ptr rewriterView = std::make_unique( + view->externalDependencies(), RewriterView::Amend); rewriterView->setCheckSemanticErrors(false); rewriterView->setTextModifier(&modifier); rewriterView->setAllowComponentRoot(true); rewriterView->setPossibleImportsEnabled(false); - inputModel->setRewriterView(rewriterView.data()); + inputModel->setRewriterView(rewriterView.get()); if (rewriterView->errors().isEmpty() && rewriterView->rootModelNode().isValid()) { ModelNode rootModelNode = rewriterView->rootModelNode(); - inputModel->detachView(rewriterView.data()); + inputModel->detachView(rewriterView.get()); QmlVisualNode(rootModelNode).setPosition(position); ModelMerger merger(view); return merger.insertModel(rootModelNode); @@ -329,7 +330,7 @@ QmlObjectNode QmlVisualNode::createQmlObjectNode(AbstractView *view, { QmlObjectNode newQmlObjectNode; - NodeHints hints = NodeHints::fromItemLibraryEntry(itemLibraryEntry); + NodeHints hints = NodeHints::fromItemLibraryEntry(itemLibraryEntry, view->model()); auto createNodeFunc = [=, &newQmlObjectNode, &parentProperty]() { #ifndef QDS_USE_PROJECTSTORAGE @@ -361,13 +362,17 @@ QmlObjectNode QmlVisualNode::createQmlObjectNode(AbstractView *view, propertyPairList.append(position.propertyPairList()); ModelNode::NodeSourceType nodeSourceType = ModelNode::NodeWithoutSource; - if (itemLibraryEntry.typeName() == "QtQml.Component") - nodeSourceType = ModelNode::NodeWithComponentSource; #ifdef QDS_USE_PROJECTSTORAGE + NodeMetaInfo metaInfo{itemLibraryEntry.typeId(), view->model()->projectStorage()}; + if (metaInfo.isQmlComponent()) + nodeSourceType = ModelNode::NodeWithComponentSource; newQmlObjectNode = QmlObjectNode(view->createModelNode( itemLibraryEntry.typeName(), propertyPairList, {}, {}, nodeSourceType)); #else + if (itemLibraryEntry.typeName() == "QtQml.Component") + nodeSourceType = ModelNode::NodeWithComponentSource; + newQmlObjectNode = QmlObjectNode(view->createModelNode(itemLibraryEntry.typeName(), majorVersion, minorVersion, diff --git a/src/plugins/qmldesigner/designercore/model/rewriteactioncompressor.cpp b/src/plugins/qmldesigner/designercore/model/rewriteactioncompressor.cpp index c4d96bb2505..edee6840ef6 100644 --- a/src/plugins/qmldesigner/designercore/model/rewriteactioncompressor.cpp +++ b/src/plugins/qmldesigner/designercore/model/rewriteactioncompressor.cpp @@ -58,7 +58,6 @@ void RewriteActionCompressor::compressImports(QList &actions) c actionsToRemove.append(action); actionsToRemove.append(addImportAction); addedImports.remove(import); - delete addImportAction; } else { removedImports.insert(import, action); } @@ -67,13 +66,11 @@ void RewriteActionCompressor::compressImports(QList &actions) c if (RewriteAction *duplicateAction = addedImports.value(import, 0)) { actionsToRemove.append(duplicateAction); addedImports.remove(import); - delete duplicateAction; addedImports.insert(import, action); } else if (RewriteAction *removeAction = removedImports.value(import, 0)) { actionsToRemove.append(action); actionsToRemove.append(removeAction); removedImports.remove(import); - delete removeAction; } else { addedImports.insert(import, action); } diff --git a/src/plugins/qmldesigner/designercore/model/rewriterview.cpp b/src/plugins/qmldesigner/designercore/model/rewriterview.cpp index 88a3de7e094..3c481573d2e 100644 --- a/src/plugins/qmldesigner/designercore/model/rewriterview.cpp +++ b/src/plugins/qmldesigner/designercore/model/rewriterview.cpp @@ -16,12 +16,14 @@ #include #include #include +#include #include +#include +#include +#include #include #include #include -#include -#include #include #include @@ -56,9 +58,9 @@ RewriterView::RewriterView(ExternalDependenciesInterface &externalDependencies, DifferenceHandling differenceHandling) : AbstractView{externalDependencies} , m_differenceHandling(differenceHandling) - , m_positionStorage(new ModelNodePositionStorage) - , m_modelToTextMerger(new Internal::ModelToTextMerger(this)) - , m_textToModelMerger(new Internal::TextToModelMerger(this)) + , m_positionStorage(std::make_unique()) + , m_modelToTextMerger(std::make_unique(this)) + , m_textToModelMerger(std::make_unique(this)) { m_amendTimer.setSingleShot(true); @@ -78,12 +80,12 @@ RewriterView::~RewriterView() = default; Internal::ModelToTextMerger *RewriterView::modelToTextMerger() const { - return m_modelToTextMerger.data(); + return m_modelToTextMerger.get(); } Internal::TextToModelMerger *RewriterView::textToModelMerger() const { - return m_textToModelMerger.data(); + return m_textToModelMerger.get(); } void RewriterView::modelAttached(Model *model) @@ -92,7 +94,7 @@ void RewriterView::modelAttached(Model *model) AbstractView::modelAttached(model); - ModelAmender differenceHandler(m_textToModelMerger.data()); + ModelAmender differenceHandler(m_textToModelMerger.get()); const QString qmlSource = m_textModifier->text(); if (m_textToModelMerger->load(qmlSource, differenceHandler)) m_lastCorrectQmlSource = qmlSource; @@ -104,6 +106,7 @@ void RewriterView::modelAttached(Model *model) m_modelAttachPending = true; QTimer::singleShot(1000, this, [this, model](){ modelAttached(model); + restoreAuxiliaryData(); }); } } @@ -492,7 +495,7 @@ void RewriterView::amendQmlText() const QString newQmlText = m_textModifier->text(); - ModelAmender differenceHandler(m_textToModelMerger.data()); + ModelAmender differenceHandler(m_textToModelMerger.get()); if (m_textToModelMerger->load(newQmlText, differenceHandler)) m_lastCorrectQmlSource = newQmlText; emitCustomNotification(EndRewriterAmend); @@ -698,7 +701,7 @@ void RewriterView::forceAmend() Internal::ModelNodePositionStorage *RewriterView::positionStorage() const { - return m_positionStorage.data(); + return m_positionStorage.get(); } QList RewriterView::warnings() const @@ -755,7 +758,7 @@ void RewriterView::resetToLastCorrectQml() { m_textModifier->textDocument()->undo(); m_textModifier->textDocument()->clearUndoRedoStacks(QTextDocument::RedoStack); - ModelAmender differenceHandler(m_textToModelMerger.data()); + ModelAmender differenceHandler(m_textToModelMerger.get()); Internal::WriteLocker::unlock(model()); m_textToModelMerger->load(m_textModifier->text(), differenceHandler); Internal::WriteLocker::lock(model()); @@ -1004,6 +1007,61 @@ QSet > RewriterView::qrcMapping() const return m_textToModelMerger->qrcMapping(); } +namespace { +#ifdef QDS_USE_PROJECTSTORAGE + +ModuleIds generateModuleIds(const ModelNodes &nodes) +{ + ModuleIds moduleIds; + moduleIds.reserve(Utils::usize(nodes)); + for (const auto &node : nodes) { + auto exportedNames = node.metaInfo().allExportedTypeNames(); + if (exportedNames.size()) + moduleIds.push_back(exportedNames.front().moduleId); + } + + std::sort(moduleIds.begin(), moduleIds.end()); + moduleIds.erase(std::unique(moduleIds.begin(), moduleIds.end()), moduleIds.end()); + + return moduleIds; +} + +QStringList generateImports(ModuleIds moduleIds, const ProjectStorageType &projectStorage) +{ + QStringList imports; + imports.reserve(std::ssize(moduleIds)); + + for (auto moduleId : moduleIds) { + using Storage::ModuleKind; + auto module = projectStorage.module(moduleId); + switch (module.kind) { + case ModuleKind::QmlLibrary: + imports.push_back("import " + module.name.toQString()); + break; + case ModuleKind::PathLibrary: + imports.push_back("import \"" + module.name.toQString() + "\""); + break; + case ModuleKind::CppLibrary: + break; + } + } + + return imports; +} + +QStringList generateImports(const ModelNodes &nodes) +{ + if (nodes.empty()) + return {}; + + auto moduleIds = generateModuleIds(nodes); + + return generateImports(moduleIds, *nodes.front().model()->projectStorage()); +} + +#endif +} // namespace + void RewriterView::moveToComponent(const ModelNode &modelNode) { if (!modelNode.isValid()) @@ -1012,20 +1070,26 @@ void RewriterView::moveToComponent(const ModelNode &modelNode) int offset = nodeOffset(modelNode); const QList nodes = modelNode.allSubModelNodesAndThisNode(); - QSet directPaths; +#ifdef QDS_USE_PROJECTSTORAGE + auto directPaths = generateImports(nodes); +#else + QSet directPathsSet; // Always add QtQuick import QString quickImport = model()->qtQuickItemMetaInfo().requiredImportString(); if (!quickImport.isEmpty()) - directPaths.insert(quickImport); + directPathsSet.insert(quickImport); for (const ModelNode &partialNode : nodes) { QString importStr = partialNode.metaInfo().requiredImportString(); if (importStr.size()) - directPaths << importStr; + directPathsSet << importStr; } - QString importData = Utils::sorted(directPaths.values()).join(QChar::LineFeed); + auto directPaths = directPathsSet.values(); +#endif + + QString importData = Utils::sorted(directPaths).join(QChar::LineFeed); if (importData.size()) importData.append(QString(2, QChar::LineFeed)); @@ -1091,7 +1155,7 @@ void RewriterView::qmlTextChanged() switch (m_differenceHandling) { case Validate: { - ModelValidator differenceHandler(m_textToModelMerger.data()); + ModelValidator differenceHandler(m_textToModelMerger.get()); if (m_textToModelMerger->load(newQmlText, differenceHandler)) m_lastCorrectQmlSource = newQmlText; break; diff --git a/src/plugins/qmldesigner/designercore/model/stylesheetmerger.cpp b/src/plugins/qmldesigner/designercore/model/stylesheetmerger.cpp index 16877b61db6..4f744d54e6c 100644 --- a/src/plugins/qmldesigner/designercore/model/stylesheetmerger.cpp +++ b/src/plugins/qmldesigner/designercore/model/stylesheetmerger.cpp @@ -23,6 +23,8 @@ #include #include +#include + namespace { QPoint pointForModelNode(const QmlDesigner::ModelNode &node) @@ -641,10 +643,10 @@ void StylesheetMerger::styleMerge(const QString &qmlTemplateString, textEditTemplate.setPlainText(imports + qmlTemplateString); NotIndentingTextEditModifier textModifierTemplate(&textEditTemplate); - QScopedPointer templateRewriterView( - new RewriterView(externalDependencies, RewriterView::Amend)); + std::unique_ptr templateRewriterView = std::make_unique( + externalDependencies, RewriterView::Amend); templateRewriterView->setTextModifier(&textModifierTemplate); - templateModel->attachView(templateRewriterView.data()); + templateModel->attachView(templateRewriterView.get()); templateRewriterView->setCheckSemanticErrors(false); templateRewriterView->setPossibleImportsEnabled(false); @@ -665,12 +667,12 @@ void StylesheetMerger::styleMerge(const QString &qmlTemplateString, textEditStyle.setPlainText(parentRewriterView->textModifierContent()); NotIndentingTextEditModifier textModifierStyle(&textEditStyle); - QScopedPointer styleRewriterView( - new RewriterView(externalDependencies, RewriterView::Amend)); + std::unique_ptr styleRewriterView = std::make_unique( + externalDependencies, RewriterView::Amend); styleRewriterView->setTextModifier(&textModifierStyle); - styleModel->attachView(styleRewriterView.data()); + styleModel->attachView(styleRewriterView.get()); - StylesheetMerger merger(templateRewriterView.data(), styleRewriterView.data()); + StylesheetMerger merger(templateRewriterView.get(), styleRewriterView.get()); try { merger.merge(); diff --git a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp index 1c1aba5feb9..b66270dca12 100644 --- a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp +++ b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp @@ -94,21 +94,23 @@ bool isGlobalQtEnums(QStringView value) bool isKnownEnumScopes(QStringView value) { - static constexpr auto list = Utils::to_array({u"TextInput", - u"TextEdit", - u"Material", - u"Universal", - u"Font", - u"Shape", - u"ShapePath", - u"AbstractButton", - u"Text", - u"ShaderEffectSource", - u"Grid", - u"ItemLayer", - u"ImageLayer", - u"SpriteLayer", - u"Light"}); + static constexpr auto list = Utils::to_array( + {u"TextInput", + u"TextEdit", + u"Material", + u"Universal", + u"Font", + u"Shape", + u"ShapePath", + u"AbstractButton", + u"Text", + u"ShaderEffectSource", + u"Grid", + u"ItemLayer", + u"ImageLayer", + u"SpriteLayer", + u"Light", + u"ExtendedSceneEnvironment.GlowBlendMode"}); return std::find(std::begin(list), std::end(list), QmlDesigner::ModelUtils::toStdStringView(value)) != std::end(list); @@ -559,6 +561,11 @@ public: //Check for known enum scopes used globally if (isKnownEnumScopes(astValueList.constFirst())) return QVariant::fromValue(Enumeration(astValue)); + } else if (astValueList.size() == 3) { + QString enumName = astValueList.constFirst() + '.' + astValueList.at(1); + if (isKnownEnumScopes(enumName)) + return QVariant::fromValue( + Enumeration(enumName.toUtf8(), astValueList.constLast().toUtf8())); } auto eStmt = AST::cast(rhs); diff --git a/src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h b/src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h index 35658c005f1..76305b1fbee 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/commontypecache.h @@ -84,7 +84,6 @@ inline constexpr char PrincipledMaterial[] = "PrincipledMaterial"; inline constexpr char PropertyAnimation[] = "PropertyAnimation"; inline constexpr char PropertyChanges[] = "PropertyChanges"; inline constexpr char QML[] = "QML"; -inline constexpr char QML_cppnative[] = "QML-cppnative"; inline constexpr char QQuick3DParticleAbstractShape[] = "QQuick3DParticleAbstractShape"; inline constexpr char QQuickStateOperation[] = "QQuickStateOperation"; inline constexpr char QtMultimedia[] = "QtMultimedia"; @@ -94,7 +93,6 @@ inline constexpr char QtQml_Models[] = "QtQml.Models"; inline constexpr char QtQml_XmlListModel[] = "QtQml.XmlListModel"; inline constexpr char QtQuick3D[] = "QtQuick3D"; inline constexpr char QtQuick3D_Particles3D[] = "QtQuick3D.Particles3D"; -inline constexpr char QtQuick3D_Particles3D_cppnative[] = "QtQuick3D.Particles3D-cppnative"; inline constexpr char QtQuick[] = "QtQuick"; inline constexpr char QtQuick_Controls[] = "QtQuick.Controls"; inline constexpr char QtQuick_Dialogs[] = "QtQuick.Dialogs"; @@ -104,7 +102,6 @@ inline constexpr char QtQuick_Studio_Components[] = "QtQuick.Studio.Components"; inline constexpr char QtQuick_Templates[] = "QtQuick.Templates"; inline constexpr char QtQuick_Timeline[] = "QtQuick.Timeline"; inline constexpr char QtQuick_Window[] = "QtQuick.Window"; -inline constexpr char QtQuick_cppnative[] = "QtQuick-cppnative"; inline constexpr char Qt_SafeRenderer[] = "Qt.SafeRenderer"; inline constexpr char Rectangle[] = "Rectangle"; inline constexpr char Repeater[] = "Repeater"; @@ -149,7 +146,7 @@ struct BaseCacheType QmlDesigner::TypeId typeId; }; -template +template struct CacheType : public BaseCacheType { }; @@ -157,106 +154,107 @@ struct CacheType : public BaseCacheType template class CommonTypeCache { - using CommonTypes = std::tuple, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType, - CacheType>; + using CommonTypes = std::tuple< + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType, + CacheType>; public: CommonTypeCache(const ProjectStorage &projectStorage) @@ -283,14 +281,14 @@ public: std::fill(std::begin(m_typesWithoutProperties), std ::end(m_typesWithoutProperties), TypeId{}); } - template + template TypeId typeId() const { - auto &type = std::get>(m_types); + auto &type = std::get>(m_types); if (type.typeId) return type.typeId; - return refreshTypedId(type, moduleName, typeName); + return refreshTypedId(type, moduleName, moduleKind, typeName); } template @@ -307,11 +305,11 @@ public: else if constexpr (std::is_same_v) return typeId(); else if constexpr (std::is_same_v) - return typeId(); + return typeId(); else if constexpr (std::is_same_v) return typeId(); else if constexpr (std::is_same_v) - return typeId(); + return typeId(); else if constexpr (std::is_same_v) return typeId(); else if constexpr (std::is_same_v) @@ -341,10 +339,11 @@ public: private: TypeId refreshTypedId(BaseCacheType &type, ::Utils::SmallStringView moduleName, + ModuleKind moduleKind, ::Utils::SmallStringView typeName) const { if (!type.moduleId) - type.moduleId = m_projectStorage.moduleId(moduleName); + type.moduleId = m_projectStorage.moduleId(moduleName, moduleKind); type.typeId = m_projectStorage.typeId(type.moduleId, typeName, Storage::Version{}); @@ -353,10 +352,11 @@ private: TypeId refreshTypedIdWithoutTransaction(BaseCacheType &type, ::Utils::SmallStringView moduleName, - ::Utils::SmallStringView typeName) const + ::Utils::SmallStringView typeName, + ModuleKind moduleKind) const { if (!type.moduleId) - type.moduleId = m_projectStorage.fetchModuleIdUnguarded(moduleName); + type.moduleId = m_projectStorage.fetchModuleIdUnguarded(moduleName, moduleKind); type.typeId = m_projectStorage.fetchTypeIdByModuleIdAndExportedName(type.moduleId, typeName); @@ -371,26 +371,27 @@ private: std::copy(std::begin(typeIds), std::end(typeIds), std::begin(m_typesWithoutProperties)); } - template + template TypeId typeIdWithoutTransaction() const { - auto &type = std::get>(m_types); + auto &type = std::get>(m_types); if (type.typeId) return type.typeId; - return refreshTypedIdWithoutTransaction(type, moduleName, typeName); + return refreshTypedIdWithoutTransaction(type, moduleName, typeName, moduleKind); } void updateTypeIdsWithoutProperties() { - setupTypeIdsWithoutProperties({typeIdWithoutTransaction(), - typeIdWithoutTransaction(), - typeIdWithoutTransaction(), - typeIdWithoutTransaction(), - typeIdWithoutTransaction(), - typeIdWithoutTransaction(), - typeIdWithoutTransaction(), - typeIdWithoutTransaction()}); + setupTypeIdsWithoutProperties( + {typeIdWithoutTransaction(), + typeIdWithoutTransaction(), + typeIdWithoutTransaction(), + typeIdWithoutTransaction(), + typeIdWithoutTransaction(), + typeIdWithoutTransaction(), + typeIdWithoutTransaction(), + typeIdWithoutTransaction()}); } private: diff --git a/src/plugins/qmldesigner/designercore/projectstorage/filesystem.cpp b/src/plugins/qmldesigner/designercore/projectstorage/filesystem.cpp index 1376b2c3d9d..d11190fdc74 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/filesystem.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/filesystem.cpp @@ -11,6 +11,7 @@ #include #include +#include #include namespace QmlDesigner { @@ -69,6 +70,18 @@ QString FileSystem::contentAsQString(const QString &filePath) const return {}; } +QStringList FileSystem::subdirectories(const QString &directoryPath) const +{ + QStringList directoryPaths; + directoryPaths.reserve(100); + QDirIterator directoryIterator{directoryPath, QDir::Dirs | QDir::NoDotAndDotDot}; + + while (directoryIterator.hasNext()) + directoryPaths.push_back(directoryIterator.next()); + + return directoryPaths; +} + void FileSystem::remove(const SourceIds &sourceIds) { for (SourceId sourceId : sourceIds) diff --git a/src/plugins/qmldesigner/designercore/projectstorage/filesystem.h b/src/plugins/qmldesigner/designercore/projectstorage/filesystem.h index 28754a8560b..1c881741c6a 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/filesystem.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/filesystem.h @@ -31,6 +31,7 @@ public: long long lastModified(SourceId sourceId) const override; FileStatus fileStatus(SourceId sourceId) const override; QString contentAsQString(const QString &filePath) const override; + QStringList subdirectories(const QString &directoryPath) const override; void remove(const SourceIds &sourceIds) override; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/filesysteminterface.h b/src/plugins/qmldesigner/designercore/projectstorage/filesysteminterface.h index 6a7c964fa61..ff7608c9a3f 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/filesysteminterface.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/filesysteminterface.h @@ -20,6 +20,7 @@ public: virtual FileStatus fileStatus(SourceId sourceId) const = 0; virtual void remove(const SourceIds &sourceIds) = 0; virtual QString contentAsQString(const QString &filePath) const = 0; + virtual QStringList subdirectories(const QString &directoryPath) const = 0; protected: ~FileSystemInterface() = default; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp index a7577d3ab77..2283b64945a 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.cpp @@ -7,6 +7,25 @@ namespace QmlDesigner { +enum class SpecialIdState { Unresolved = -1 }; + +constexpr TypeId unresolvedTypeId = TypeId::createSpecialState(SpecialIdState::Unresolved); + +class UnresolvedTypeId : public TypeId +{ +public: + constexpr UnresolvedTypeId() + : TypeId{TypeId::createSpecialState(SpecialIdState::Unresolved)} + {} + + static constexpr UnresolvedTypeId create(DatabaseType idNumber) + { + UnresolvedTypeId id; + id.id = idNumber; + return id; + } +}; + struct ProjectStorage::Statements { Statements(Sqlite::Database &database) @@ -17,9 +36,13 @@ struct ProjectStorage::Statements Sqlite::ReadWriteStatement<1, 2> insertTypeStatement{ "INSERT OR IGNORE INTO types(sourceId, name) VALUES(?1, ?2) RETURNING typeId", database}; Sqlite::WriteStatement<5> updatePrototypeAndExtensionStatement{ - "UPDATE types SET prototypeId=?2, prototypeNameId=?3, extensionId=?4, extensionNameId=?5 " - "WHERE typeId=?1 AND (prototypeId IS NOT ?2 OR extensionId IS NOT ?3 AND prototypeId " - "IS NOT ?4 OR extensionNameId IS NOT ?5)", + "UPDATE types " + "SET prototypeId=?2, prototypeNameId=?3, extensionId=?4, extensionNameId=?5 " + "WHERE typeId=?1 AND ( " + " prototypeId IS NOT ?2 " + " OR extensionId IS NOT ?3 " + " OR prototypeId IS NOT ?4 " + " OR extensionNameId IS NOT ?5)", database}; mutable Sqlite::ReadStatement<1, 1> selectTypeIdByExportedNameStatement{ "SELECT typeId FROM exportedTypeNames WHERE name=?1", database}; @@ -90,7 +113,19 @@ struct ProjectStorage::Statements Sqlite::WriteStatement<2> updateTypeTraitStatement{ "UPDATE types SET traits = ?2 WHERE typeId=?1", database}; Sqlite::WriteStatement<2> updateTypeAnnotationTraitStatement{ - "UPDATE types SET annotationTraits = ?2 WHERE typeId=?1", database}; + "WITH RECURSIVE " + " typeSelection(typeId) AS (" + " VALUES(?1) " + " UNION ALL " + " SELECT t.typeId " + " FROM types AS t JOIN typeSelection AS ts " + " WHERE prototypeId=ts.typeId " + " AND t.typeId NOT IN (SELECT typeId FROM typeAnnotations)) " + "UPDATE types AS t " + "SET annotationTraits = ?2 " + "FROM typeSelection ts " + "WHERE t.typeId=ts.typeId", + database}; Sqlite::ReadStatement<1, 2> selectNotUpdatedTypesInSourcesStatement{ "SELECT DISTINCT typeId FROM types WHERE (sourceId IN carray(?1) AND typeId NOT IN " "carray(?2))", @@ -235,14 +270,14 @@ struct ProjectStorage::Statements database}; Sqlite::WriteStatement<1> deleteEnumerationDeclarationStatement{ "DELETE FROM enumerationDeclarations WHERE enumerationDeclarationId=?", database}; - mutable Sqlite::ReadStatement<1, 1> selectModuleIdByNameStatement{ - "SELECT moduleId FROM modules WHERE name=? LIMIT 1", database}; - mutable Sqlite::ReadWriteStatement<1, 1> insertModuleNameStatement{ - "INSERT INTO modules(name) VALUES(?1) RETURNING moduleId", database}; - mutable Sqlite::ReadStatement<1, 1> selectModuleNameStatement{ - "SELECT name FROM modules WHERE moduleId =?1", database}; - mutable Sqlite::ReadStatement<2> selectAllModulesStatement{"SELECT name, moduleId FROM modules", - database}; + mutable Sqlite::ReadStatement<1, 2> selectModuleIdByNameStatement{ + "SELECT moduleId FROM modules WHERE kind=?1 AND name=?2 LIMIT 1", database}; + mutable Sqlite::ReadWriteStatement<1, 2> insertModuleNameStatement{ + "INSERT INTO modules(kind, name) VALUES(?1, ?2) RETURNING moduleId", database}; + mutable Sqlite::ReadStatement<2, 1> selectModuleStatement{ + "SELECT name, kind FROM modules WHERE moduleId =?1", database}; + mutable Sqlite::ReadStatement<3> selectAllModulesStatement{ + "SELECT name, kind, moduleId FROM modules", database}; mutable Sqlite::ReadStatement<1, 2> selectTypeIdBySourceIdAndNameStatement{ "SELECT typeId FROM types WHERE sourceId=?1 and name=?2", database}; mutable Sqlite::ReadStatement<1, 3> selectTypeIdByModuleIdsAndExportedNameStatement{ @@ -345,13 +380,51 @@ struct ProjectStorage::Statements "SELECT name FROM propertyDeclarations WHERE propertyDeclarationId=?", database}; Sqlite::WriteStatement<2> updatePropertyDeclarationTypeStatement{ "UPDATE propertyDeclarations SET propertyTypeId=?2 WHERE propertyDeclarationId=?1", database}; - Sqlite::ReadWriteStatement<2, 1> updatePrototypeIdToNullStatement{ - "UPDATE types SET prototypeId=NULL WHERE prototypeId=?1 RETURNING " - "typeId, prototypeNameId", + Sqlite::ReadWriteStatement<2, 2> updatePrototypeIdToTypeIdStatement{ + "UPDATE types " + "SET prototypeId=?2 " + "WHERE prototypeId=?1 " + "RETURNING typeId, prototypeNameId", database}; - Sqlite::ReadWriteStatement<2, 1> updateExtensionIdToNullStatement{ - "UPDATE types SET extensionId=NULL WHERE extensionId=?1 RETURNING " - "typeId, extensionNameId", + Sqlite::ReadWriteStatement<2, 2> updateExtensionIdToTypeIdStatement{ + "UPDATE types " + "SET extensionId=?2 " + "WHERE extensionId=?1 " + "RETURNING typeId, extensionNameId", + database}; + Sqlite::ReadStatement<2, 2> selectTypeIdAndPrototypeNameIdForPrototypeIdAndTypeNameStatement{ + "SELECT typeId, prototypeNameId " + "FROM types " + "WHERE prototypeNameId IN ( " + " SELECT importedTypeNameId " + " FROM " + " importedTypeNames WHERE name=?1) " + " AND prototypeId=?2", + database}; + Sqlite::ReadStatement<2, 2> selectTypeIdAndPrototypeNameIdForPrototypeIdAndSourceIdStatement{ + "SELECT typeId , prototypeNameId " + "FROM types " + "WHERE prototypeId=?1 AND sourceId=?2", + database}; + Sqlite::ReadStatement<2, 2> selectTypeIdAndExtensionNameIdForExtensionIdAndSourceIdStatement{ + "SELECT typeId, extensionNameId " + "FROM types " + "WHERE extensionId=?1 AND sourceId=?2", + database}; + Sqlite::ReadWriteStatement<3, 3> updatePrototypeIdAndExtensionIdToTypeIdForSourceIdStatement{ + "UPDATE types " + "SET prototypeId=?2, extensionId=?3 " + "WHERE sourceId=?1 " + "RETURNING typeId, prototypeNameId, extensionNameId", + database}; + Sqlite::ReadStatement<2, 2> selectTypeIdForExtensionIdAndTypeNameStatement{ + "SELECT typeId , prototypeNameId " + "FROM types " + "WHERE extensionNameId IN ( " + " SELECT importedTypeNameId " + " FROM importedTypeNames " + " WHERE name=?1) " + " AND extensionId=?2", database}; Sqlite::WriteStatement<2> updateTypePrototypeStatement{ "UPDATE types SET prototypeId=?2 WHERE typeId=?1", database}; @@ -490,25 +563,31 @@ struct ProjectStorage::Statements "DELETE FROM exportedTypeNames WHERE exportedTypeNameId=?", database}; Sqlite::WriteStatement<2> updateExportedTypeNameTypeIdStatement{ "UPDATE exportedTypeNames SET typeId=?2 WHERE exportedTypeNameId=?1", database}; - mutable Sqlite::ReadStatement<4, 1> selectProjectDatasForSourceIdsStatement{ - "SELECT projectSourceId, sourceId, moduleId, fileType FROM projectDatas WHERE " - "projectSourceId IN carray(?1) ORDER BY projectSourceId, sourceId", + mutable Sqlite::ReadStatement<4, 1> selectDirectoryInfosForSourceIdsStatement{ + "SELECT directorySourceId, sourceId, moduleId, fileType FROM directoryInfos WHERE " + "directorySourceId IN carray(?1) ORDER BY directorySourceId, sourceId", database}; - Sqlite::WriteStatement<4> insertProjectDataStatement{ - "INSERT INTO projectDatas(projectSourceId, sourceId, " + Sqlite::WriteStatement<4> insertDirectoryInfoStatement{ + "INSERT INTO directoryInfos(directorySourceId, sourceId, " "moduleId, fileType) VALUES(?1, ?2, ?3, ?4)", database}; - Sqlite::WriteStatement<2> deleteProjectDataStatement{ - "DELETE FROM projectDatas WHERE projectSourceId=?1 AND sourceId=?2", database}; - Sqlite::WriteStatement<4> updateProjectDataStatement{ - "UPDATE projectDatas SET moduleId=?3, fileType=?4 WHERE projectSourceId=?1 AND sourceId=?2", + Sqlite::WriteStatement<2> deleteDirectoryInfoStatement{ + "DELETE FROM directoryInfos WHERE directorySourceId=?1 AND sourceId=?2", database}; + Sqlite::WriteStatement<4> updateDirectoryInfoStatement{ + "UPDATE directoryInfos SET moduleId=?3, fileType=?4 WHERE directorySourceId=?1 AND sourceId=?2", database}; - mutable Sqlite::ReadStatement<4, 1> selectProjectDatasForSourceIdStatement{ - "SELECT projectSourceId, sourceId, moduleId, fileType FROM projectDatas WHERE " - "projectSourceId=?1", + mutable Sqlite::ReadStatement<4, 1> selectDirectoryInfosForSourceIdStatement{ + "SELECT directorySourceId, sourceId, moduleId, fileType FROM directoryInfos WHERE " + "directorySourceId=?1", database}; - mutable Sqlite::ReadStatement<4, 1> selectProjectDataForSourceIdStatement{ - "SELECT projectSourceId, sourceId, moduleId, fileType FROM projectDatas WHERE " + mutable Sqlite::ReadStatement<4, 2> selectDirectoryInfosForSourceIdAndFileTypeStatement{ + "SELECT directorySourceId, sourceId, moduleId, fileType FROM directoryInfos WHERE " + "directorySourceId=?1 AND fileType=?2", + database}; + mutable Sqlite::ReadStatement<1, 2> selectDirectoryInfosSourceIdsForSourceIdAndFileTypeStatement{ + "SELECT sourceId FROM directoryInfos WHERE directorySourceId=?1 AND fileType=?2", database}; + mutable Sqlite::ReadStatement<4, 1> selectDirectoryInfoForSourceIdStatement{ + "SELECT directorySourceId, sourceId, moduleId, fileType FROM directoryInfos WHERE " "sourceId=?1 LIMIT 1", database}; mutable Sqlite::ReadStatement<1, 1> selectTypeIdsForSourceIdsStatement{ @@ -601,6 +680,13 @@ struct ProjectStorage::Statements "UPDATE types SET defaultPropertyId=NULL WHERE defaultPropertyId=?1", database}; mutable Sqlite::ReadStatement<3, 1> selectInfoTypeByTypeIdStatement{ "SELECT sourceId, traits, annotationTraits FROM types WHERE typeId=?", database}; + mutable Sqlite::ReadStatement<1, 1> selectSourceIdByTypeIdStatement{ + "SELECT sourceId FROM types WHERE typeId=?", database}; + mutable Sqlite::ReadStatement<1, 1> selectPrototypeAnnotationTraitsByTypeIdStatement{ + "SELECT annotationTraits " + "FROM types " + "WHERE typeId=(SELECT prototypeId FROM types WHERE typeId=?)", + database}; mutable Sqlite::ReadStatement<1, 1> selectDefaultPropertyDeclarationIdStatement{ "SELECT defaultPropertyId FROM types WHERE typeId=?", database}; mutable Sqlite::ReadStatement<1, 1> selectPrototypeIdsForTypeIdInOrderStatement{ @@ -639,17 +725,21 @@ struct ProjectStorage::Statements database}; Sqlite::WriteStatement<1> deletePropertyEditorPathStatement{ "DELETE FROM propertyEditorPaths WHERE typeId=?1", database}; - mutable Sqlite::ReadStatement<4, 1> selectTypeAnnotationsForSourceIdsStatement{ - "SELECT typeId, iconPath, itemLibrary, hints FROM typeAnnotations WHERE " + mutable Sqlite::ReadStatement<5, 1> selectTypeAnnotationsForSourceIdsStatement{ + "SELECT typeId, typeName, iconPath, itemLibrary, hints FROM typeAnnotations WHERE " "sourceId IN carray(?1) ORDER BY typeId", database}; - Sqlite::WriteStatement<6> insertTypeAnnotationStatement{ + Sqlite::WriteStatement<7> insertTypeAnnotationStatement{ "INSERT INTO " - " typeAnnotations(typeId, sourceId, directorySourceId, iconPath, itemLibrary, hints) " - "VALUES(?1, ?2, ?3, ?4, ?5, ?6)", + " typeAnnotations(typeId, sourceId, directorySourceId, typeName, iconPath, itemLibrary, " + " hints) " + "VALUES(?1, ?2, ?3, ?4, ?5, ?6, ?7)", + database}; + Sqlite::WriteStatement<5> updateTypeAnnotationStatement{ + "UPDATE typeAnnotations " + "SET typeName=?2, iconPath=?3, itemLibrary=?4, hints=?5 " + "WHERE typeId=?1", database}; - Sqlite::WriteStatement<4> updateTypeAnnotationStatement{ - "UPDATE typeAnnotations SET iconPath=?2, itemLibrary=?3, hints=?4 WHERE typeId=?1", database}; Sqlite::WriteStatement<1> deleteTypeAnnotationStatement{ "DELETE FROM typeAnnotations WHERE typeId=?1", database}; mutable Sqlite::ReadStatement<1, 1> selectTypeIconPathStatement{ @@ -663,22 +753,22 @@ struct ProjectStorage::Statements "SELECT sourceId FROM typeAnnotations WHERE directorySourceId=?1 ORDER BY sourceId", database}; mutable Sqlite::ReadStatement<1, 0> selectTypeAnnotationDirectorySourceIdsStatement{ "SELECT DISTINCT directorySourceId FROM typeAnnotations ORDER BY directorySourceId", database}; - mutable Sqlite::ReadStatement<9> selectItemLibraryEntriesStatement{ - "SELECT typeId, i.value->>'$.name', i.value->>'$.iconPath', i.value->>'$.category', " - " i.value->>'$.import', i.value->>'$.toolTip', i.value->>'$.properties', " - " i.value->>'$.extraFilePaths', i.value->>'$.templatePath' " + mutable Sqlite::ReadStatement<10> selectItemLibraryEntriesStatement{ + "SELECT typeId, typeName, i.value->>'$.name', i.value->>'$.iconPath', " + " i.value->>'$.category', i.value->>'$.import', i.value->>'$.toolTip', " + " i.value->>'$.properties', i.value->>'$.extraFilePaths', i.value->>'$.templatePath' " "FROM typeAnnotations AS ta , json_each(ta.itemLibrary) AS i " "WHERE ta.itemLibrary IS NOT NULL", database}; - mutable Sqlite::ReadStatement<9, 1> selectItemLibraryEntriesByTypeIdStatement{ - "SELECT typeId, i.value->>'$.name', i.value->>'$.iconPath', i.value->>'$.category', " - " i.value->>'$.import', i.value->>'$.toolTip', i.value->>'$.properties', " - " i.value->>'$.extraFilePaths', i.value->>'$.templatePath' " + mutable Sqlite::ReadStatement<10, 1> selectItemLibraryEntriesByTypeIdStatement{ + "SELECT typeId, typeName, i.value->>'$.name', i.value->>'$.iconPath', " + " i.value->>'$.category', i.value->>'$.import', i.value->>'$.toolTip', " + " i.value->>'$.properties', i.value->>'$.extraFilePaths', i.value->>'$.templatePath' " "FROM typeAnnotations AS ta, json_each(ta.itemLibrary) AS i " "WHERE typeId=?1 AND ta.itemLibrary IS NOT NULL", database}; - mutable Sqlite::ReadStatement<9, 1> selectItemLibraryEntriesBySourceIdStatement{ - "SELECT typeId, i.value->>'$.name', i.value->>'$.iconPath', " + mutable Sqlite::ReadStatement<10, 1> selectItemLibraryEntriesBySourceIdStatement{ + "SELECT typeId, typeName, i.value->>'$.name', i.value->>'$.iconPath', " "i.value->>'$.category', " " i.value->>'$.import', i.value->>'$.toolTip', i.value->>'$.properties', " " i.value->>'$.extraFilePaths', i.value->>'$.templatePath' " @@ -724,7 +814,7 @@ public: createModuleExportedImportsTable(database, moduleIdColumn); createDocumentImportsTable(database, moduleIdColumn); createFileStatusesTable(database); - createProjectDatasTable(database); + createDirectoryInfosTable(database); createPropertyEditorPathsTable(database); createTypeAnnotionsTable(database); } @@ -774,23 +864,23 @@ public: auto &sourceIdColumn = typesTable.addColumn("sourceId", Sqlite::StrictColumnType::Integer); auto &typesNameColumn = typesTable.addColumn("name", Sqlite::StrictColumnType::Text); typesTable.addColumn("traits", Sqlite::StrictColumnType::Integer); - auto &prototypeIdColumn = typesTable.addForeignKeyColumn("prototypeId", - typesTable, - Sqlite::ForeignKeyAction::NoAction, - Sqlite::ForeignKeyAction::Restrict); - typesTable.addColumn("prototypeNameId", Sqlite::StrictColumnType::Integer); - auto &extensionIdColumn = typesTable.addForeignKeyColumn("extensionId", - typesTable, - Sqlite::ForeignKeyAction::NoAction, - Sqlite::ForeignKeyAction::Restrict); - typesTable.addColumn("extensionNameId", Sqlite::StrictColumnType::Integer); + auto &prototypeIdColumn = typesTable.addColumn("prototypeId", + Sqlite::StrictColumnType::Integer); + auto &prototypeNameIdColumn = typesTable.addColumn("prototypeNameId", + Sqlite::StrictColumnType::Integer); + auto &extensionIdColumn = typesTable.addColumn("extensionId", + Sqlite::StrictColumnType::Integer); + auto &extensionNameIdColumn = typesTable.addColumn("extensionNameId", + Sqlite::StrictColumnType::Integer); auto &defaultPropertyIdColumn = typesTable.addColumn("defaultPropertyId", Sqlite::StrictColumnType::Integer); typesTable.addColumn("annotationTraits", Sqlite::StrictColumnType::Integer); typesTable.addUniqueIndex({sourceIdColumn, typesNameColumn}); typesTable.addIndex({defaultPropertyIdColumn}); - typesTable.addIndex({prototypeIdColumn}); - typesTable.addIndex({extensionIdColumn}); + typesTable.addIndex({prototypeIdColumn, sourceIdColumn}); + typesTable.addIndex({extensionIdColumn, sourceIdColumn}); + typesTable.addIndex({prototypeNameIdColumn}); + typesTable.addIndex({extensionNameIdColumn}); typesTable.initialize(database); @@ -942,9 +1032,10 @@ public: auto &modelIdColumn = table.addColumn("moduleId", Sqlite::StrictColumnType::Integer, {Sqlite::PrimaryKey{}}); + auto &kindColumn = table.addColumn("kind", Sqlite::StrictColumnType::Integer); auto &nameColumn = table.addColumn("name", Sqlite::StrictColumnType::Text); - table.addUniqueIndex({nameColumn}); + table.addUniqueIndex({kindColumn, nameColumn}); table.initialize(database); @@ -1041,20 +1132,21 @@ public: table.initialize(database); } - void createProjectDatasTable(Database &database) + void createDirectoryInfosTable(Database &database) { Sqlite::StrictTable table; table.setUseIfNotExists(true); table.setUseWithoutRowId(true); - table.setName("projectDatas"); - auto &projectSourceIdColumn = table.addColumn("projectSourceId", + table.setName("directoryInfos"); + auto &directorySourceIdColumn = table.addColumn("directorySourceId", Sqlite::StrictColumnType::Integer); auto &sourceIdColumn = table.addColumn("sourceId", Sqlite::StrictColumnType::Integer); table.addColumn("moduleId", Sqlite::StrictColumnType::Integer); - table.addColumn("fileType", Sqlite::StrictColumnType::Integer); + auto &fileTypeColumn = table.addColumn("fileType", Sqlite::StrictColumnType::Integer); - table.addPrimaryKeyContraint({projectSourceIdColumn, sourceIdColumn}); + table.addPrimaryKeyContraint({directorySourceIdColumn, sourceIdColumn}); table.addUniqueIndex({sourceIdColumn}); + table.addIndex({directorySourceIdColumn, fileTypeColumn}); table.initialize(database); } @@ -1086,7 +1178,7 @@ public: auto &sourceIdColumn = table.addColumn("sourceId", Sqlite::StrictColumnType::Integer); auto &directorySourceIdColumn = table.addColumn("directorySourceId", Sqlite::StrictColumnType::Integer); - + table.addColumn("typeName", Sqlite::StrictColumnType::Text); table.addColumn("iconPath", Sqlite::StrictColumnType::Text); table.addColumn("itemLibrary", Sqlite::StrictColumnType::Text); table.addColumn("hints", Sqlite::StrictColumnType::Text); @@ -1098,8 +1190,11 @@ public: } }; -ProjectStorage::ProjectStorage(Database &database, bool isInitialized) +ProjectStorage::ProjectStorage(Database &database, + ProjectStorageErrorNotifierInterface &errorNotifier, + bool isInitialized) : database{database} + , errorNotifier{&errorNotifier} , exclusiveTransaction{database} , initializer{std::make_unique(database, isInitialized)} , moduleCache{ModuleStorageAdapter{*this}} @@ -1143,7 +1238,9 @@ void ProjectStorage::synchronize(Storage::Synchronization::SynchronizationPackag package.moduleDependencies, package.updatedModuleDependencySourceIds, package.moduleExportedImports, - package.updatedModuleIds); + package.updatedModuleIds, + relinkablePrototypes, + relinkableExtensions); synchronizeTypes(package.types, updatedTypeIds, insertedAliasPropertyDeclarations, @@ -1174,7 +1271,7 @@ void ProjectStorage::synchronize(Storage::Synchronization::SynchronizationPackag linkAliases(insertedAliasPropertyDeclarations, updatedAliasPropertyDeclarations); - synchronizeProjectDatas(package.projectDatas, package.updatedProjectSourceIds); + synchronizeDirectoryInfos(package.directoryInfos, package.updatedDirectoryInfoSourceIds); commonTypeCache_.resetTypeIds(); }); @@ -1191,7 +1288,24 @@ void ProjectStorage::synchronizeDocumentImports(Storage::Imports imports, Source keyValue("source id", sourceId)}; Sqlite::withImmediateTransaction(database, [&] { - synchronizeDocumentImports(imports, {sourceId}, Storage::Synchronization::ImportKind::Import); + AliasPropertyDeclarations relinkableAliasPropertyDeclarations; + PropertyDeclarations relinkablePropertyDeclarations; + Prototypes relinkablePrototypes; + Prototypes relinkableExtensions; + TypeIds deletedTypeIds; + + synchronizeDocumentImports(imports, + {sourceId}, + Storage::Synchronization::ImportKind::Import, + Relink::Yes, + relinkablePrototypes, + relinkableExtensions); + + relink(relinkableAliasPropertyDeclarations, + relinkablePropertyDeclarations, + relinkablePrototypes, + relinkableExtensions, + deletedTypeIds); }); } @@ -1207,21 +1321,21 @@ void ProjectStorage::removeObserver(ProjectStorageObserver *observer) observers.removeOne(observer); } -ModuleId ProjectStorage::moduleId(Utils::SmallStringView moduleName) const +ModuleId ProjectStorage::moduleId(Utils::SmallStringView moduleName, Storage::ModuleKind kind) const { using NanotraceHR::keyValue; NanotraceHR::Tracer tracer{"get module id"_t, projectStorageCategory(), keyValue("module name", moduleName)}; - auto moduleId = moduleCache.id(moduleName); + auto moduleId = moduleCache.id({moduleName, kind}); tracer.end(keyValue("module id", moduleId)); return moduleId; } -Utils::SmallString ProjectStorage::moduleName(ModuleId moduleId) const +Storage::Module ProjectStorage::module(ModuleId moduleId) const { using NanotraceHR::keyValue; NanotraceHR::Tracer tracer{"get module name"_t, @@ -1231,11 +1345,12 @@ Utils::SmallString ProjectStorage::moduleName(ModuleId moduleId) const if (!moduleId) throw ModuleDoesNotExists{}; - auto moduleName = moduleCache.value(moduleId); + auto module = moduleCache.value(moduleId); - tracer.end(keyValue("module name", moduleName)); + tracer.end(keyValue("module name", module.name)); + tracer.end(keyValue("module kind", module.kind)); - return moduleName; + return {module.name, module.kind}; } TypeId ProjectStorage::typeId(ModuleId moduleId, @@ -1570,6 +1685,7 @@ Storage::Info::ItemLibraryEntries ProjectStorage::itemLibraryEntries(TypeId type Storage::Info::ItemLibraryEntries entries; auto callback = [&](TypeId typeId_, + Utils::SmallStringView typeName, Utils::SmallStringView name, Utils::SmallStringView iconPath, Utils::SmallStringView category, @@ -1578,7 +1694,8 @@ Storage::Info::ItemLibraryEntries ProjectStorage::itemLibraryEntries(TypeId type Utils::SmallStringView properties, Utils::SmallStringView extraFilePaths, Utils::SmallStringView templatePath) { - auto &last = entries.emplace_back(typeId_, name, iconPath, category, import, toolTip, templatePath); + auto &last = entries.emplace_back( + typeId_, typeName, name, iconPath, category, import, toolTip, templatePath); if (properties.size()) s->selectItemLibraryPropertiesStatement.readTo(last.properties, properties); if (extraFilePaths.size()) @@ -1603,6 +1720,7 @@ Storage::Info::ItemLibraryEntries ProjectStorage::itemLibraryEntries(ImportId im Storage::Info::ItemLibraryEntries entries; auto callback = [&](TypeId typeId_, + Utils::SmallStringView typeName, Utils::SmallStringView name, Utils::SmallStringView iconPath, Utils::SmallStringView category, @@ -1611,7 +1729,8 @@ Storage::Info::ItemLibraryEntries ProjectStorage::itemLibraryEntries(ImportId im Utils::SmallStringView properties, Utils::SmallStringView extraFilePaths, Utils::SmallStringView templatePath) { - auto &last = entries.emplace_back(typeId_, name, iconPath, category, import, toolTip, templatePath); + auto &last = entries.emplace_back( + typeId_, typeName, name, iconPath, category, import, toolTip, templatePath); if (properties.size()) s->selectItemLibraryPropertiesStatement.readTo(last.properties, properties); if (extraFilePaths.size()) @@ -1636,6 +1755,7 @@ Storage::Info::ItemLibraryEntries ProjectStorage::itemLibraryEntries(SourceId so Storage::Info::ItemLibraryEntries entries; auto callback = [&](TypeId typeId, + Utils::SmallStringView typeName, Utils::SmallStringView name, Utils::SmallStringView iconPath, Utils::SmallStringView category, @@ -1644,7 +1764,8 @@ Storage::Info::ItemLibraryEntries ProjectStorage::itemLibraryEntries(SourceId so Utils::SmallStringView properties, Utils::SmallStringView extraFilePaths, Utils::SmallStringView templatePath) { - auto &last = entries.emplace_back(typeId, name, iconPath, category, import, toolTip, templatePath); + auto &last = entries.emplace_back( + typeId, typeName, name, iconPath, category, import, toolTip, templatePath); if (properties.size()) s->selectItemLibraryPropertiesStatement.readTo(last.properties, properties); if (extraFilePaths.size()) @@ -1667,6 +1788,7 @@ Storage::Info::ItemLibraryEntries ProjectStorage::allItemLibraryEntries() const Storage::Info::ItemLibraryEntries entries; auto callback = [&](TypeId typeId, + Utils::SmallStringView typeName, Utils::SmallStringView name, Utils::SmallStringView iconPath, Utils::SmallStringView category, @@ -1675,7 +1797,8 @@ Storage::Info::ItemLibraryEntries ProjectStorage::allItemLibraryEntries() const Utils::SmallStringView properties, Utils::SmallStringView extraFilePaths, Utils::SmallStringView templatePath) { - auto &last = entries.emplace_back(typeId, name, iconPath, category, import, toolTip, templatePath); + auto &last = entries.emplace_back( + typeId, typeName, name, iconPath, category, import, toolTip, templatePath); if (properties.size()) s->selectItemLibraryPropertiesStatement.readTo(last.properties, properties); if (extraFilePaths.size()) @@ -2082,53 +2205,87 @@ FileStatus ProjectStorage::fetchFileStatus(SourceId sourceId) const return fileStatus; } -std::optional ProjectStorage::fetchProjectData(SourceId sourceId) const +std::optional ProjectStorage::fetchDirectoryInfo(SourceId sourceId) const { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch project data"_t, + NanotraceHR::Tracer tracer{"fetch directory info"_t, projectStorageCategory(), keyValue("source id", sourceId)}; - auto projectData = s->selectProjectDataForSourceIdStatement - .optionalValueWithTransaction( + auto directoryInfo = s->selectDirectoryInfoForSourceIdStatement + .optionalValueWithTransaction( sourceId); - tracer.end(keyValue("project data", projectData)); + tracer.end(keyValue("directory info", directoryInfo)); - return projectData; + return directoryInfo; } -Storage::Synchronization::ProjectDatas ProjectStorage::fetchProjectDatas(SourceId projectSourceId) const +Storage::Synchronization::DirectoryInfos ProjectStorage::fetchDirectoryInfos(SourceId directorySourceId) const { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch project datas by source id"_t, + NanotraceHR::Tracer tracer{"fetch directory infos by source id"_t, projectStorageCategory(), - keyValue("source id", projectSourceId)}; + keyValue("source id", directorySourceId)}; - auto projectDatas = s->selectProjectDatasForSourceIdStatement - .valuesWithTransaction( - projectSourceId); + auto directoryInfos = s->selectDirectoryInfosForSourceIdStatement + .valuesWithTransaction( + directorySourceId); - tracer.end(keyValue("project datas", projectDatas)); + tracer.end(keyValue("directory infos", directoryInfos)); - return projectDatas; + return directoryInfos; } -Storage::Synchronization::ProjectDatas ProjectStorage::fetchProjectDatas( - const SourceIds &projectSourceIds) const +Storage::Synchronization::DirectoryInfos ProjectStorage::fetchDirectoryInfos( + SourceId directorySourceId, Storage::Synchronization::FileType fileType) const { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch project datas by source ids"_t, + NanotraceHR::Tracer tracer{"fetch directory infos by source id and file type"_t, projectStorageCategory(), - keyValue("source ids", projectSourceIds)}; + keyValue("source id", directorySourceId), + keyValue("file type", fileType)}; - auto projectDatas = s->selectProjectDatasForSourceIdsStatement - .valuesWithTransaction( - toIntegers(projectSourceIds)); + auto directoryInfos = s->selectDirectoryInfosForSourceIdAndFileTypeStatement + .valuesWithTransaction( + directorySourceId, fileType); - tracer.end(keyValue("project datas", projectDatas)); + tracer.end(keyValue("directory infos", directoryInfos)); - return projectDatas; + return directoryInfos; +} + +Storage::Synchronization::DirectoryInfos ProjectStorage::fetchDirectoryInfos( + const SourceIds &directorySourceIds) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch directory infos by source ids"_t, + projectStorageCategory(), + keyValue("source ids", directorySourceIds)}; + + auto directoryInfos = s->selectDirectoryInfosForSourceIdsStatement + .valuesWithTransaction( + toIntegers(directorySourceIds)); + + tracer.end(keyValue("directory infos", directoryInfos)); + + return directoryInfos; +} + +SmallSourceIds<32> ProjectStorage::fetchSubdirectorySourceIds(SourceId directorySourceId) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"fetch subdirectory source ids"_t, + projectStorageCategory(), + keyValue("source id", directorySourceId)}; + + auto sourceIds = s->selectDirectoryInfosSourceIdsForSourceIdAndFileTypeStatement + .valuesWithTransaction>( + directorySourceId, Storage::Synchronization::FileType::Directory); + + tracer.end(keyValue("source ids", sourceIds)); + + return sourceIds; } void ProjectStorage::setPropertyEditorPathId(TypeId typeId, SourceId pathId) @@ -2168,20 +2325,17 @@ void ProjectStorage::resetForTestsOnly() moduleCache.clearForTestOnly(); } -bool ProjectStorage::moduleNameLess(Utils::SmallStringView first, Utils::SmallStringView second) noexcept -{ - return first < second; -} - -ModuleId ProjectStorage::fetchModuleId(Utils::SmallStringView moduleName) +ModuleId ProjectStorage::fetchModuleId(Utils::SmallStringView moduleName, + Storage::ModuleKind moduleKind) { using NanotraceHR::keyValue; NanotraceHR::Tracer tracer{"fetch module id"_t, projectStorageCategory(), - keyValue("module name", moduleName)}; + keyValue("module name", moduleName), + keyValue("module kind", moduleKind)}; auto moduleId = Sqlite::withDeferredTransaction(database, [&] { - return fetchModuleIdUnguarded(moduleName); + return fetchModuleIdUnguarded(moduleName, moduleKind); }); tracer.end(keyValue("module id", moduleId)); @@ -2189,26 +2343,26 @@ ModuleId ProjectStorage::fetchModuleId(Utils::SmallStringView moduleName) return moduleId; } -Utils::PathString ProjectStorage::fetchModuleName(ModuleId id) +Storage::Module ProjectStorage::fetchModule(ModuleId id) { using NanotraceHR::keyValue; NanotraceHR::Tracer tracer{"fetch module name"_t, projectStorageCategory(), keyValue("module id", id)}; - auto moduleName = Sqlite::withDeferredTransaction(database, - [&] { return fetchModuleNameUnguarded(id); }); + auto module = Sqlite::withDeferredTransaction(database, [&] { return fetchModuleUnguarded(id); }); - tracer.end(keyValue("module name", moduleName)); + tracer.end(keyValue("module name", module.name)); + tracer.end(keyValue("module name", module.kind)); - return moduleName; + return module; } -ProjectStorage::Modules ProjectStorage::fetchAllModules() const +ProjectStorage::ModuleCacheEntries ProjectStorage::fetchAllModules() const { NanotraceHR::Tracer tracer{"fetch all modules"_t, projectStorageCategory()}; - return s->selectAllModulesStatement.valuesWithTransaction(); + return s->selectAllModulesStatement.valuesWithTransaction(); } void ProjectStorage::callRefreshMetaInfoCallback(const TypeIds &deletedTypeIds) @@ -2277,12 +2431,6 @@ void ProjectStorage::updateTypeIdInTypeAnnotations(Storage::Synchronization::Typ annotation.typeName); } - for (auto &annotation : typeAnnotations) { - if (!annotation.typeId) - qWarning() << moduleName(annotation.moduleId).toQString() - << annotation.typeName.toQString(); - } - typeAnnotations.erase(std::remove_if(typeAnnotations.begin(), typeAnnotations.end(), [](const auto &annotation) { return !annotation.typeId; }), @@ -2311,7 +2459,6 @@ void ProjectStorage::synchronizeTypeAnnotations(Storage::Synchronization::TypeAn if (!annotation.sourceId) throw TypeAnnotationHasInvalidSourceId{}; - synchronizeTypeTraits(annotation.typeId, annotation.traits); using NanotraceHR::keyValue; NanotraceHR::Tracer tracer{"insert type annotations"_t, @@ -2321,16 +2468,19 @@ void ProjectStorage::synchronizeTypeAnnotations(Storage::Synchronization::TypeAn s->insertTypeAnnotationStatement.write(annotation.typeId, annotation.sourceId, annotation.directorySourceId, + annotation.typeName, annotation.iconPath, createEmptyAsNull(annotation.itemLibraryJson), createEmptyAsNull(annotation.hintsJson)); + + synchronizeTypeTraits(annotation.typeId, annotation.traits); }; auto update = [&](const TypeAnnotationView &annotationFromDatabase, const TypeAnnotation &annotation) { - synchronizeTypeTraits(annotation.typeId, annotation.traits); - if (annotationFromDatabase.iconPath != annotation.iconPath + if (annotationFromDatabase.typeName != annotation.typeName + || annotationFromDatabase.iconPath != annotation.iconPath || annotationFromDatabase.itemLibraryJson != annotation.itemLibraryJson || annotationFromDatabase.hintsJson != annotation.hintsJson) { using NanotraceHR::keyValue; @@ -2341,24 +2491,33 @@ void ProjectStorage::synchronizeTypeAnnotations(Storage::Synchronization::TypeAn keyValue("type annotation", annotation)}; s->updateTypeAnnotationStatement.write(annotation.typeId, + annotation.typeName, annotation.iconPath, createEmptyAsNull(annotation.itemLibraryJson), createEmptyAsNull(annotation.hintsJson)); + + synchronizeTypeTraits(annotation.typeId, annotation.traits); + return Sqlite::UpdateChange::Update; } + synchronizeTypeTraits(annotation.typeId, annotation.traits); + return Sqlite::UpdateChange::No; }; auto remove = [&](const TypeAnnotationView &annotationFromDatabase) { - synchronizeTypeTraits(annotationFromDatabase.typeId, Storage::TypeTraits{}); - using NanotraceHR::keyValue; NanotraceHR::Tracer tracer{"remove type annotations"_t, projectStorageCategory(), keyValue("type annotation", annotationFromDatabase)}; + auto prototypeAnnotationTraits = s->selectPrototypeAnnotationTraitsByTypeIdStatement + .value(annotationFromDatabase.typeId); s->deleteTypeAnnotationStatement.write(annotationFromDatabase.typeId); + + s->updateTypeAnnotationTraitStatement.write(annotationFromDatabase.typeId, + prototypeAnnotationTraits); }; Sqlite::insertUpdateDelete(range, typeAnnotations, compareKey, insert, update, remove); @@ -2432,74 +2591,75 @@ void ProjectStorage::synchronizeTypes(Storage::Synchronization::Types &types, syncDefaultProperties(types); } -void ProjectStorage::synchronizeProjectDatas(Storage::Synchronization::ProjectDatas &projectDatas, - const SourceIds &updatedProjectSourceIds) +void ProjectStorage::synchronizeDirectoryInfos(Storage::Synchronization::DirectoryInfos &directoryInfos, + const SourceIds &updatedDirectoryInfoSourceIds) { - NanotraceHR::Tracer tracer{"synchronize project datas"_t, projectStorageCategory()}; + NanotraceHR::Tracer tracer{"synchronize directory infos"_t, projectStorageCategory()}; auto compareKey = [](auto &&first, auto &&second) { - auto projectSourceIdDifference = first.projectSourceId - second.projectSourceId; - if (projectSourceIdDifference != 0) - return projectSourceIdDifference; + auto directorySourceIdDifference = first.directorySourceId - second.directorySourceId; + if (directorySourceIdDifference != 0) + return directorySourceIdDifference; return first.sourceId - second.sourceId; }; - std::sort(projectDatas.begin(), projectDatas.end(), [&](auto &&first, auto &&second) { - return std::tie(first.projectSourceId, first.sourceId) - < std::tie(second.projectSourceId, second.sourceId); + std::sort(directoryInfos.begin(), directoryInfos.end(), [&](auto &&first, auto &&second) { + return std::tie(first.directorySourceId, first.sourceId) + < std::tie(second.directorySourceId, second.sourceId); }); - auto range = s->selectProjectDatasForSourceIdsStatement.range( - toIntegers(updatedProjectSourceIds)); + auto range = s->selectDirectoryInfosForSourceIdsStatement.range( + toIntegers(updatedDirectoryInfoSourceIds)); - auto insert = [&](const Storage::Synchronization::ProjectData &projectData) { + auto insert = [&](const Storage::Synchronization::DirectoryInfo &directoryInfo) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"insert project data"_t, + NanotraceHR::Tracer tracer{"insert directory info"_t, projectStorageCategory(), - keyValue("project data", projectData)}; + keyValue("directory info", directoryInfo)}; - if (!projectData.projectSourceId) - throw ProjectDataHasInvalidProjectSourceId{}; - if (!projectData.sourceId) - throw ProjectDataHasInvalidSourceId{}; + if (!directoryInfo.directorySourceId) + throw DirectoryInfoHasInvalidProjectSourceId{}; + if (!directoryInfo.sourceId) + throw DirectoryInfoHasInvalidSourceId{}; - s->insertProjectDataStatement.write(projectData.projectSourceId, - projectData.sourceId, - projectData.moduleId, - projectData.fileType); + s->insertDirectoryInfoStatement.write(directoryInfo.directorySourceId, + directoryInfo.sourceId, + directoryInfo.moduleId, + directoryInfo.fileType); }; - auto update = [&](const Storage::Synchronization::ProjectData &projectDataFromDatabase, - const Storage::Synchronization::ProjectData &projectData) { - if (projectDataFromDatabase.fileType != projectData.fileType - || !compareInvalidAreTrue(projectDataFromDatabase.moduleId, projectData.moduleId)) { + auto update = [&](const Storage::Synchronization::DirectoryInfo &directoryInfoFromDatabase, + const Storage::Synchronization::DirectoryInfo &directoryInfo) { + if (directoryInfoFromDatabase.fileType != directoryInfo.fileType + || !compareInvalidAreTrue(directoryInfoFromDatabase.moduleId, directoryInfo.moduleId)) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"update project data"_t, + NanotraceHR::Tracer tracer{"update directory info"_t, projectStorageCategory(), - keyValue("project data", projectData), - keyValue("project data from database", projectDataFromDatabase)}; + keyValue("directory info", directoryInfo), + keyValue("directory info from database", + directoryInfoFromDatabase)}; - s->updateProjectDataStatement.write(projectData.projectSourceId, - projectData.sourceId, - projectData.moduleId, - projectData.fileType); + s->updateDirectoryInfoStatement.write(directoryInfo.directorySourceId, + directoryInfo.sourceId, + directoryInfo.moduleId, + directoryInfo.fileType); return Sqlite::UpdateChange::Update; } return Sqlite::UpdateChange::No; }; - auto remove = [&](const Storage::Synchronization::ProjectData &projectData) { + auto remove = [&](const Storage::Synchronization::DirectoryInfo &directoryInfo) { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"remove project data"_t, + NanotraceHR::Tracer tracer{"remove directory info"_t, projectStorageCategory(), - keyValue("project data", projectData)}; + keyValue("directory info", directoryInfo)}; - s->deleteProjectDataStatement.write(projectData.projectSourceId, projectData.sourceId); + s->deleteDirectoryInfoStatement.write(directoryInfo.directorySourceId, directoryInfo.sourceId); }; - Sqlite::insertUpdateDelete(range, projectDatas, compareKey, insert, update, remove); + Sqlite::insertUpdateDelete(range, directoryInfos, compareKey, insert, update, remove); } void ProjectStorage::synchronizeFileStatuses(FileStatuses &fileStatuses, @@ -2564,19 +2724,29 @@ void ProjectStorage::synchronizeImports(Storage::Imports &imports, Storage::Imports &moduleDependencies, const SourceIds &updatedModuleDependencySourceIds, Storage::Synchronization::ModuleExportedImports &moduleExportedImports, - const ModuleIds &updatedModuleIds) + const ModuleIds &updatedModuleIds, + Prototypes &relinkablePrototypes, + Prototypes &relinkableExtensions) { NanotraceHR::Tracer tracer{"synchronize imports"_t, projectStorageCategory()}; synchromizeModuleExportedImports(moduleExportedImports, updatedModuleIds); NanotraceHR::Tracer importTracer{"synchronize qml document imports"_t, projectStorageCategory()}; - synchronizeDocumentImports(imports, updatedSourceIds, Storage::Synchronization::ImportKind::Import); + synchronizeDocumentImports(imports, + updatedSourceIds, + Storage::Synchronization::ImportKind::Import, + Relink::No, + relinkablePrototypes, + relinkableExtensions); importTracer.end(); NanotraceHR::Tracer moduleDependenciesTracer{"synchronize module depdencies"_t, projectStorageCategory()}; synchronizeDocumentImports(moduleDependencies, updatedModuleDependencySourceIds, - Storage::Synchronization::ImportKind::ModuleDependency); + Storage::Synchronization::ImportKind::ModuleDependency, + Relink::Yes, + relinkablePrototypes, + relinkableExtensions); moduleDependenciesTracer.end(); } @@ -2650,38 +2820,41 @@ void ProjectStorage::synchromizeModuleExportedImports( Sqlite::insertUpdateDelete(range, moduleExportedImports, compareKey, insert, update, remove); } -ModuleId ProjectStorage::fetchModuleIdUnguarded(Utils::SmallStringView name) const +ModuleId ProjectStorage::fetchModuleIdUnguarded(Utils::SmallStringView name, + Storage::ModuleKind kind) const { using NanotraceHR::keyValue; NanotraceHR::Tracer tracer{"fetch module id ungarded"_t, projectStorageCategory(), - keyValue("module name", name)}; + keyValue("module name", name), + keyValue("module kind", kind)}; - auto moduleId = s->selectModuleIdByNameStatement.value(name); + auto moduleId = s->selectModuleIdByNameStatement.value(kind, name); if (!moduleId) - moduleId = s->insertModuleNameStatement.value(name); + moduleId = s->insertModuleNameStatement.value(kind, name); tracer.end(keyValue("module id", moduleId)); return moduleId; } -Utils::PathString ProjectStorage::fetchModuleNameUnguarded(ModuleId id) const +Storage::Module ProjectStorage::fetchModuleUnguarded(ModuleId id) const { using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"fetch module name ungarded"_t, + NanotraceHR::Tracer tracer{"fetch module ungarded"_t, projectStorageCategory(), keyValue("module id", id)}; - auto moduleName = s->selectModuleNameStatement.value(id); + auto module = s->selectModuleStatement.value(id); - if (moduleName.empty()) + if (!module) throw ModuleDoesNotExists{}; - tracer.end(keyValue("module name", moduleName)); + tracer.end(keyValue("module name", module.name)); + tracer.end(keyValue("module name", module.kind)); - return moduleName; + return module; } void ProjectStorage::handleAliasPropertyDeclarationsWithPropertyType( @@ -2740,11 +2913,30 @@ void ProjectStorage::handlePrototypes(TypeId prototypeId, Prototypes &relinkable keyValue("type id", prototypeId), keyValue("relinkable prototypes", relinkablePrototypes)}; + auto callback = [&](TypeId typeId, ImportedTypeNameId prototypeNameId) { + if (prototypeNameId) + relinkablePrototypes.emplace_back(typeId, prototypeNameId); + }; + + s->updatePrototypeIdToTypeIdStatement.readCallback(callback, prototypeId, unresolvedTypeId); +} + +void ProjectStorage::handlePrototypesWithExportedTypeNameAndTypeId( + Utils::SmallStringView exportedTypeName, TypeId typeId, Prototypes &relinkablePrototypes) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"handle invalid prototypes"_t, + projectStorageCategory(), + keyValue("type id", exportedTypeName), + keyValue("relinkable prototypes", relinkablePrototypes)}; + auto callback = [&](TypeId typeId, ImportedTypeNameId prototypeNameId) { relinkablePrototypes.emplace_back(typeId, prototypeNameId); }; - s->updatePrototypeIdToNullStatement.readCallback(callback, prototypeId); + s->selectTypeIdAndPrototypeNameIdForPrototypeIdAndTypeNameStatement.readCallback(callback, + exportedTypeName, + typeId); } void ProjectStorage::handleExtensions(TypeId extensionId, Prototypes &relinkableExtensions) @@ -2755,11 +2947,28 @@ void ProjectStorage::handleExtensions(TypeId extensionId, Prototypes &relinkable keyValue("type id", extensionId), keyValue("relinkable extensions", relinkableExtensions)}; + auto callback = [&](TypeId typeId, ImportedTypeNameId extensionNameId) { + if (extensionNameId) + relinkableExtensions.emplace_back(typeId, extensionNameId); + }; + + s->updateExtensionIdToTypeIdStatement.readCallback(callback, extensionId, unresolvedTypeId); +} + +void ProjectStorage::handleExtensionsWithExportedTypeNameAndTypeId( + Utils::SmallStringView exportedTypeName, TypeId typeId, Prototypes &relinkableExtensions) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"handle invalid extensions"_t, + projectStorageCategory(), + keyValue("type id", exportedTypeName), + keyValue("relinkable extensions", relinkableExtensions)}; + auto callback = [&](TypeId typeId, ImportedTypeNameId extensionNameId) { relinkableExtensions.emplace_back(typeId, extensionNameId); }; - s->updateExtensionIdToNullStatement.readCallback(callback, extensionId); + s->selectTypeIdForExtensionIdAndTypeNameStatement.readCallback(callback, exportedTypeName, typeId); } void ProjectStorage::deleteType(TypeId typeId, @@ -2846,6 +3055,39 @@ void ProjectStorage::relinkPropertyDeclarations(PropertyDeclarations &relinkable TypeCompare{}); } +template +void ProjectStorage::relinkPrototypes(Prototypes &relinkablePrototypes, + const TypeIds &deletedTypeIds, + Callable updateStatement) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"relink prototypes"_t, + projectStorageCategory(), + keyValue("relinkable prototypes", relinkablePrototypes), + keyValue("deleted type ids", deletedTypeIds)}; + + std::sort(relinkablePrototypes.begin(), relinkablePrototypes.end()); + relinkablePrototypes.erase(std::unique(relinkablePrototypes.begin(), relinkablePrototypes.end()), + relinkablePrototypes.end()); + + Utils::set_greedy_difference( + relinkablePrototypes.cbegin(), + relinkablePrototypes.cend(), + deletedTypeIds.begin(), + deletedTypeIds.end(), + [&](const Prototype &prototype) { + TypeId prototypeId = fetchTypeId(prototype.prototypeNameId); + + if (!prototypeId) + errorNotifier->typeNameCannotBeResolved(fetchImportedTypeName(prototype.prototypeNameId), + fetchTypeSourceId(prototype.typeId)); + + updateStatement(prototype.typeId, prototypeId); + checkForPrototypeChainCycle(prototype.typeId); + }, + TypeCompare{}); +} + void ProjectStorage::deleteNotUpdatedTypes(const TypeIds &updatedTypeIds, const SourceIds &updatedSourceIds, const TypeIds &typeIdsToBeDeleted, @@ -3060,6 +3302,9 @@ void ProjectStorage::synchronizeExportedTypes(const TypeIds &updatedTypeIds, } catch (const Sqlite::ConstraintPreventsModification &) { throw QmlDesigner::ExportedTypeCannotBeInserted{type.name}; } + + handlePrototypesWithExportedTypeNameAndTypeId(type.name, unresolvedTypeId, relinkablePrototypes); + handleExtensionsWithExportedTypeNameAndTypeId(type.name, unresolvedTypeId, relinkableExtensions); }; auto update = [&](const Storage::Synchronization::ExportedTypeView &view, @@ -3095,6 +3340,7 @@ void ProjectStorage::synchronizeExportedTypes(const TypeIds &updatedTypeIds, relinkableAliasPropertyDeclarations); handlePrototypes(view.typeId, relinkablePrototypes); handleExtensions(view.typeId, relinkableExtensions); + s->deleteExportedTypeNameStatement.write(view.exportedTypeNameId); }; @@ -3410,11 +3656,90 @@ void ProjectStorage::resetRemovedAliasPropertyDeclarationsToNull( PropertyCompare{}); } +void ProjectStorage::handlePrototypesWithSourceIdAndPrototypeId(SourceId sourceId, + TypeId prototypeId, + Prototypes &relinkablePrototypes) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"handle prototypes with source id and prototype id"_t, + projectStorageCategory(), + keyValue("source id", sourceId), + keyValue("type id", prototypeId)}; + + auto callback = [&](TypeId typeId, ImportedTypeNameId prototypeNameId) { + if (prototypeNameId) + relinkablePrototypes.emplace_back(typeId, prototypeNameId); + }; + + s->selectTypeIdAndPrototypeNameIdForPrototypeIdAndSourceIdStatement.readCallback(callback, + prototypeId, + sourceId); +} + +void ProjectStorage::handlePrototypesAndExtensionsWithSourceId(SourceId sourceId, + TypeId prototypeId, + TypeId extensionId, + Prototypes &relinkablePrototypes, + Prototypes &relinkableExtensions) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"handle prototypes with source id"_t, + projectStorageCategory(), + keyValue("source id", sourceId), + keyValue("prototype id", prototypeId), + keyValue("extension id", extensionId)}; + + auto callback = + [&](TypeId typeId, ImportedTypeNameId prototypeNameId, ImportedTypeNameId extensionNameId) { + if (prototypeNameId) + relinkablePrototypes.emplace_back(typeId, prototypeNameId); + if (extensionNameId) + relinkableExtensions.emplace_back(typeId, extensionNameId); + }; + + s->updatePrototypeIdAndExtensionIdToTypeIdForSourceIdStatement.readCallback(callback, + sourceId, + prototypeId, + extensionId); +} + +void ProjectStorage::handleExtensionsWithSourceIdAndExtensionId(SourceId sourceId, + TypeId extensionId, + Prototypes &relinkableExtensions) +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"handle prototypes with source id and prototype id"_t, + projectStorageCategory(), + keyValue("source id", sourceId), + keyValue("type id", extensionId)}; + + auto callback = [&](TypeId typeId, ImportedTypeNameId extensionNameId) { + if (extensionNameId) + relinkableExtensions.emplace_back(typeId, extensionNameId); + }; + + s->selectTypeIdAndExtensionNameIdForExtensionIdAndSourceIdStatement.readCallback(callback, + extensionId, + sourceId); +} + ImportId ProjectStorage::insertDocumentImport(const Storage::Import &import, Storage::Synchronization::ImportKind importKind, ModuleId sourceModuleId, - ImportId parentImportId) + ImportId parentImportId, + Relink relink, + Prototypes &relinkablePrototypes, + Prototypes &relinkableExtensions) { + if (relink == Relink::Yes) { + handlePrototypesWithSourceIdAndPrototypeId(import.sourceId, + unresolvedTypeId, + relinkablePrototypes); + handleExtensionsWithSourceIdAndExtensionId(import.sourceId, + unresolvedTypeId, + relinkableExtensions); + } + if (import.version.minor) { return s->insertDocumentImportWithVersionStatement.value(import.sourceId, import.moduleId, @@ -3441,7 +3766,10 @@ ImportId ProjectStorage::insertDocumentImport(const Storage::Import &import, void ProjectStorage::synchronizeDocumentImports(Storage::Imports &imports, const SourceIds &updatedSourceIds, - Storage::Synchronization::ImportKind importKind) + Storage::Synchronization::ImportKind importKind, + Relink relink, + Prototypes &relinkablePrototypes, + Prototypes &relinkableExtensions) { std::sort(imports.begin(), imports.end(), [](auto &&first, auto &&second) { return std::tie(first.sourceId, first.moduleId, first.version) @@ -3478,7 +3806,13 @@ void ProjectStorage::synchronizeDocumentImports(Storage::Imports &imports, keyValue("source id", import.sourceId), keyValue("module id", import.moduleId)}; - auto importId = insertDocumentImport(import, importKind, import.moduleId, ImportId{}); + auto importId = insertDocumentImport(import, + importKind, + import.moduleId, + ImportId{}, + relink, + relinkablePrototypes, + relinkableExtensions); auto callback = [&](ModuleId exportedModuleId, int majorVersion, int minorVersion) { Storage::Import additionImport{exportedModuleId, Storage::Version{majorVersion, minorVersion}, @@ -3498,7 +3832,10 @@ void ProjectStorage::synchronizeDocumentImports(Storage::Imports &imports, auto indirectImportId = insertDocumentImport(additionImport, exportedImportKind, import.moduleId, - importId); + importId, + relink, + relinkablePrototypes, + relinkableExtensions); tracer.end(keyValue("import id", indirectImportId)); }; @@ -3525,6 +3862,13 @@ void ProjectStorage::synchronizeDocumentImports(Storage::Imports &imports, s->deleteDocumentImportStatement.write(view.importId); s->deleteDocumentImportsWithParentImportIdStatement.write(view.sourceId, view.importId); + if (relink == Relink::Yes) { + handlePrototypesAndExtensionsWithSourceId(view.sourceId, + unresolvedTypeId, + unresolvedTypeId, + relinkablePrototypes, + relinkableExtensions); + } }; Sqlite::insertUpdateDelete(range, imports, compareKey, insert, update, remove); @@ -3550,7 +3894,7 @@ Utils::PathString ProjectStorage::createJson(const Storage::Synchronization::Par json.append("\"}"); } else { json.append(R"(","tr":)"); - json.append(Utils::SmallString::number(to_underlying(parameter.traits))); + json.append(Utils::SmallString::number(Utils::to_underlying(parameter.traits))); json.append("}"); } } @@ -4075,25 +4419,29 @@ void ProjectStorage::checkForAliasChainCycle(PropertyDeclarationId propertyDecla } std::pair ProjectStorage::fetchImportedTypeNameIdAndTypeId( - const Storage::Synchronization::ImportedTypeName &typeName, SourceId sourceId) + const Storage::Synchronization::ImportedTypeName &importedTypeName, SourceId sourceId) { using NanotraceHR::keyValue; NanotraceHR::Tracer tracer{"fetch imported type name id and type id"_t, projectStorageCategory(), - keyValue("imported type name", typeName), + keyValue("imported type name", importedTypeName), keyValue("source id", sourceId)}; TypeId typeId; ImportedTypeNameId typeNameId; - if (!std::visit([](auto &&typeName_) -> bool { return typeName_.name.isEmpty(); }, typeName)) { - typeNameId = fetchImportedTypeNameId(typeName, sourceId); + auto typeName = std::visit([](auto &&importedTypeName) { return importedTypeName.name; }, + importedTypeName); + if (!typeName.empty()) { + typeNameId = fetchImportedTypeNameId(importedTypeName, sourceId); typeId = fetchTypeId(typeNameId); tracer.end(keyValue("type id", typeId), keyValue("type name id", typeNameId)); - if (!typeId) - throw TypeNameDoesNotExists{fetchImportedTypeName(typeNameId), sourceId}; + if (!typeId) { + errorNotifier->typeNameCannotBeResolved(typeName, sourceId); + return {unresolvedTypeId, typeNameId}; + } } return {typeId, typeNameId}; @@ -4242,6 +4590,11 @@ Utils::SmallString ProjectStorage::fetchImportedTypeName(ImportedTypeNameId type return s->selectNameFromImportedTypeNamesStatement.value(typeNameId); } +SourceId ProjectStorage::fetchTypeSourceId(TypeId typeId) const +{ + return s->selectSourceIdByTypeIdStatement.value(typeId); +} + TypeId ProjectStorage::fetchTypeId(ImportedTypeNameId typeNameId, Storage::Synchronization::TypeNameKind kind) const { @@ -4253,9 +4606,10 @@ TypeId ProjectStorage::fetchTypeId(ImportedTypeNameId typeNameId, TypeId typeId; if (kind == Storage::Synchronization::TypeNameKind::Exported) { - typeId = s->selectTypeIdForImportedTypeNameNamesStatement.value(typeNameId); + typeId = s->selectTypeIdForImportedTypeNameNamesStatement.value(typeNameId); } else { - typeId = s->selectTypeIdForQualifiedImportedTypeNameNamesStatement.value(typeNameId); + typeId = s->selectTypeIdForQualifiedImportedTypeNameNamesStatement.value( + typeNameId); } tracer.end(keyValue("type id", typeId)); diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h index e7826f531b4..54d91015968 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h @@ -4,6 +4,7 @@ #pragma once #include "commontypecache.h" +#include "projectstorageerrornotifier.h" #include "projectstorageexceptions.h" #include "projectstorageinterface.h" #include "projectstoragetypes.h" @@ -38,21 +39,30 @@ class ProjectStorage final : public ProjectStorageInterface using Database = Sqlite::Database; friend Storage::Info::CommonTypeCache; + enum class Relink { No, Yes }; + public: - ProjectStorage(Database &database, bool isInitialized); + ProjectStorage(Database &database, + ProjectStorageErrorNotifierInterface &errorNotifier, + bool isInitialized); ~ProjectStorage(); void synchronize(Storage::Synchronization::SynchronizationPackage package) override; void synchronizeDocumentImports(Storage::Imports imports, SourceId sourceId) override; + void setErrorNotifier(ProjectStorageErrorNotifierInterface &errorNotifier) + { + this->errorNotifier = &errorNotifier; + } + void addObserver(ProjectStorageObserver *observer) override; void removeObserver(ProjectStorageObserver *observer) override; - ModuleId moduleId(Utils::SmallStringView moduleName) const override; + ModuleId moduleId(Utils::SmallStringView moduleName, Storage::ModuleKind kind) const override; - Utils::SmallString moduleName(ModuleId moduleId) const override; + Storage::Module module(ModuleId moduleId) const override; TypeId typeId(ModuleId moduleId, Utils::SmallStringView exportedTypeName, @@ -116,7 +126,7 @@ public: return commonTypeCache_; } - template + template TypeId commonTypeId() const { using NanotraceHR::keyValue; @@ -125,7 +135,7 @@ public: keyValue("module name", std::string_view{moduleName}), keyValue("type name", std::string_view{typeName})}; - auto typeId = commonTypeCache_.typeId(); + auto typeId = commonTypeCache_.typeId(); tracer.end(keyValue("type id", typeId)); @@ -229,11 +239,13 @@ public: FileStatus fetchFileStatus(SourceId sourceId) const override; - std::optional fetchProjectData(SourceId sourceId) const override; + std::optional fetchDirectoryInfo(SourceId sourceId) const override; - Storage::Synchronization::ProjectDatas fetchProjectDatas(SourceId projectSourceId) const override; - - Storage::Synchronization::ProjectDatas fetchProjectDatas(const SourceIds &projectSourceIds) const; + Storage::Synchronization::DirectoryInfos fetchDirectoryInfos(SourceId directorySourceId) const override; + Storage::Synchronization::DirectoryInfos fetchDirectoryInfos( + SourceId directorySourceId, Storage::Synchronization::FileType fileType) const override; + Storage::Synchronization::DirectoryInfos fetchDirectoryInfos(const SourceIds &directorySourceIds) const; + SmallSourceIds<32> fetchSubdirectorySourceIds(SourceId directorySourceId) const override; void setPropertyEditorPathId(TypeId typeId, SourceId pathId); @@ -244,50 +256,90 @@ public: void resetForTestsOnly(); private: + struct ModuleView + { + ModuleView() = default; + + ModuleView(Utils::SmallStringView name, Storage::ModuleKind kind) + : name{name} + , kind{kind} + {} + + ModuleView(const Storage::Module &module) + : name{module.name} + , kind{module.kind} + {} + + Utils::SmallStringView name; + Storage::ModuleKind kind; + + friend bool operator<(ModuleView first, ModuleView second) + { + return std::tie(first.kind, first.name) < std::tie(second.kind, second.name); + } + + friend bool operator==(const Storage::Module &first, ModuleView second) + { + return first.name == second.name && first.kind == second.kind; + } + + friend bool operator==(ModuleView first, const Storage::Module &second) + { + return second == first; + } + }; + class ModuleStorageAdapter { public: - auto fetchId(const Utils::SmallStringView name) { return storage.fetchModuleId(name); } + auto fetchId(ModuleView module) { return storage.fetchModuleId(module.name, module.kind); } - auto fetchValue(ModuleId id) { return storage.fetchModuleName(id); } + auto fetchValue(ModuleId id) { return storage.fetchModule(id); } auto fetchAll() { return storage.fetchAllModules(); } ProjectStorage &storage; }; - class Module : public StorageCacheEntry + friend ModuleStorageAdapter; + + static bool moduleNameLess(ModuleView first, ModuleView second) noexcept { - using Base = StorageCacheEntry; + return first < second; + } + + class ModuleCacheEntry : public StorageCacheEntry + { + using Base = StorageCacheEntry; public: using Base::Base; - friend bool operator==(const Module &first, const Module &second) + ModuleCacheEntry(Utils::SmallStringView name, Storage::ModuleKind kind, ModuleId moduleId) + : Base{{name, kind}, moduleId} + {} + + friend bool operator==(const ModuleCacheEntry &first, const ModuleCacheEntry &second) { return &first == &second && first.value == second.value; } + + friend bool operator==(const ModuleCacheEntry &first, ModuleView second) + { + return first.value.name == second.name && first.value.kind == second.kind; + } }; - using Modules = std::vector; + using ModuleCacheEntries = std::vector; - friend ModuleStorageAdapter; + using ModuleCache + = StorageCache; - static bool moduleNameLess(Utils::SmallStringView first, Utils::SmallStringView second) noexcept; + ModuleId fetchModuleId(Utils::SmallStringView moduleName, Storage::ModuleKind moduleKind); - using ModuleCache = StorageCache; + Storage::Module fetchModule(ModuleId id); - ModuleId fetchModuleId(Utils::SmallStringView moduleName); - - Utils::PathString fetchModuleName(ModuleId id); - - Modules fetchAllModules() const; + ModuleCacheEntries fetchAllModules() const; void callRefreshMetaInfoCallback(const TypeIds &deletedTypeIds); @@ -398,6 +450,11 @@ private: return first.typeId < second.typeId; } + friend bool operator==(Prototype first, Prototype second) + { + return first.typeId == second.typeId; + } + template friend void convertToString(String &string, const Prototype &prototype) { @@ -461,10 +518,12 @@ private: { public: TypeAnnotationView(TypeId typeId, + Utils::SmallStringView typeName, Utils::SmallStringView iconPath, Utils::SmallStringView itemLibraryJson, Utils::SmallStringView hintsJson) : typeId{typeId} + , typeName{typeName} , iconPath{iconPath} , itemLibraryJson{itemLibraryJson} , hintsJson{hintsJson} @@ -476,6 +535,7 @@ private: using NanotraceHR::dictonary; using NanotraceHR::keyValue; auto dict = dictonary(keyValue("type id", typeAnnotationView.typeId), + keyValue("type name", typeAnnotationView.typeName), keyValue("icon path", typeAnnotationView.iconPath), keyValue("item library json", typeAnnotationView.itemLibraryJson), keyValue("hints json", typeAnnotationView.hintsJson)); @@ -485,6 +545,7 @@ private: public: TypeId typeId; + Utils::SmallStringView typeName; Utils::SmallStringView iconPath; Utils::SmallStringView itemLibraryJson; Utils::PathString hintsJson; @@ -516,8 +577,8 @@ private: Prototypes &relinkableExtensions, const SourceIds &updatedSourceIds); - void synchronizeProjectDatas(Storage::Synchronization::ProjectDatas &projectDatas, - const SourceIds &updatedProjectSourceIds); + void synchronizeDirectoryInfos(Storage::Synchronization::DirectoryInfos &directoryInfos, + const SourceIds &updatedDirectoryInfoSourceIds); void synchronizeFileStatuses(FileStatuses &fileStatuses, const SourceIds &updatedSourceIds); @@ -526,15 +587,18 @@ private: Storage::Imports &moduleDependencies, const SourceIds &updatedModuleDependencySourceIds, Storage::Synchronization::ModuleExportedImports &moduleExportedImports, - const ModuleIds &updatedModuleIds); + const ModuleIds &updatedModuleIds, + Prototypes &relinkablePrototypes, + Prototypes &relinkableExtensions); void synchromizeModuleExportedImports( Storage::Synchronization::ModuleExportedImports &moduleExportedImports, const ModuleIds &updatedModuleIds); - ModuleId fetchModuleIdUnguarded(Utils::SmallStringView name) const override; + ModuleId fetchModuleIdUnguarded(Utils::SmallStringView name, + Storage::ModuleKind moduleKind) const override; - Utils::PathString fetchModuleNameUnguarded(ModuleId id) const; + Storage::Module fetchModuleUnguarded(ModuleId id) const; void handleAliasPropertyDeclarationsWithPropertyType( TypeId typeId, AliasPropertyDeclarations &relinkableAliasPropertyDeclarations); @@ -543,9 +607,14 @@ private: PropertyDeclarations &relinkablePropertyDeclarations); void handlePrototypes(TypeId prototypeId, Prototypes &relinkablePrototypes); + void handlePrototypesWithExportedTypeNameAndTypeId(Utils::SmallStringView exportedTypeName, + TypeId typeId, + Prototypes &relinkablePrototypes); void handleExtensions(TypeId extensionId, Prototypes &relinkableExtensions); - + void handleExtensionsWithExportedTypeNameAndTypeId(Utils::SmallStringView exportedTypeName, + TypeId typeId, + Prototypes &relinkableExtensions); void deleteType(TypeId typeId, AliasPropertyDeclarations &relinkableAliasPropertyDeclarations, PropertyDeclarations &relinkablePropertyDeclarations, @@ -561,32 +630,7 @@ private: template void relinkPrototypes(Prototypes &relinkablePrototypes, const TypeIds &deletedTypeIds, - Callable updateStatement) - { - using NanotraceHR::keyValue; - NanotraceHR::Tracer tracer{"relink prototypes"_t, - projectStorageCategory(), - keyValue("relinkable prototypes", relinkablePrototypes), - keyValue("deleted type ids", deletedTypeIds)}; - - std::sort(relinkablePrototypes.begin(), relinkablePrototypes.end()); - - Utils::set_greedy_difference( - relinkablePrototypes.cbegin(), - relinkablePrototypes.cend(), - deletedTypeIds.begin(), - deletedTypeIds.end(), - [&](const Prototype &prototype) { - TypeId prototypeId = fetchTypeId(prototype.prototypeNameId); - - if (!prototypeId) - throw TypeNameDoesNotExists{fetchImportedTypeName(prototype.prototypeNameId)}; - - updateStatement(prototype.typeId, prototypeId); - checkForPrototypeChainCycle(prototype.typeId); - }, - TypeCompare{}); - } + Callable updateStatement); void deleteNotUpdatedTypes(const TypeIds &updatedTypeIds, const SourceIds &updatedSourceIds, @@ -701,14 +745,32 @@ private: Storage::Synchronization::Types &types, AliasPropertyDeclarations &relinkableAliasPropertyDeclarations); + void handlePrototypesWithSourceIdAndPrototypeId(SourceId sourceId, + TypeId prototypeId, + Prototypes &relinkablePrototypes); + void handlePrototypesAndExtensionsWithSourceId(SourceId sourceId, + TypeId prototypeId, + TypeId extensionId, + Prototypes &relinkablePrototypes, + Prototypes &relinkableExtensions); + void handleExtensionsWithSourceIdAndExtensionId(SourceId sourceId, + TypeId extensionId, + Prototypes &relinkableExtensions); + ImportId insertDocumentImport(const Storage::Import &import, Storage::Synchronization::ImportKind importKind, ModuleId sourceModuleId, - ImportId parentImportId); + ImportId parentImportId, + Relink forceRelink, + Prototypes &relinkablePrototypes, + Prototypes &relinkableExtensions); void synchronizeDocumentImports(Storage::Imports &imports, const SourceIds &updatedSourceIds, - Storage::Synchronization::ImportKind importKind); + Storage::Synchronization::ImportKind importKind, + Relink forceRelink, + Prototypes &relinkablePrototypes, + Prototypes &relinkableExtensions); static Utils::PathString createJson(const Storage::Synchronization::ParameterDeclarations ¶meters); @@ -854,6 +916,7 @@ private: TypeId fetchTypeId(ImportedTypeNameId typeNameId) const; Utils::SmallString fetchImportedTypeName(ImportedTypeNameId typeNameId) const; + SourceId fetchTypeSourceId(TypeId typeId) const; TypeId fetchTypeId(ImportedTypeNameId typeNameId, Storage::Synchronization::TypeNameKind kind) const; @@ -920,6 +983,7 @@ private: public: Database &database; + ProjectStorageErrorNotifierInterface *errorNotifier = nullptr; // cannot be null Sqlite::ExclusiveNonThrowingDestructorTransaction exclusiveTransaction; std::unique_ptr initializer; mutable ModuleCache moduleCache{ModuleStorageAdapter{*this}}; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageerrornotifier.cpp b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageerrornotifier.cpp new file mode 100644 index 00000000000..a4705f5eecb --- /dev/null +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageerrornotifier.cpp @@ -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 + +#include "projectstorageerrornotifier.h" + +#include "sourcepathcache.h" + +namespace QmlDesigner { + +void ProjectStorageErrorNotifier::typeNameCannotBeResolved(Utils::SmallStringView typeName, + SourceId sourceId) +{ + qDebug() << "Missing type name: " << typeName + << " in file: " << m_pathCache.sourcePath(sourceId).toStringView(); +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageerrornotifier.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageerrornotifier.h new file mode 100644 index 00000000000..2695e930193 --- /dev/null +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageerrornotifier.h @@ -0,0 +1,25 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "projectstorageerrornotifierinterface.h" + +#include + +namespace QmlDesigner { + +class ProjectStorageErrorNotifier final : public ProjectStorageErrorNotifierInterface +{ +public: + ProjectStorageErrorNotifier(PathCacheType &pathCache) + : m_pathCache{pathCache} + {} + + void typeNameCannotBeResolved(Utils::SmallStringView typeName, SourceId souceId) override; + +private: + PathCacheType &m_pathCache; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageerrornotifierinterface.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageerrornotifierinterface.h new file mode 100644 index 00000000000..8136c9d599c --- /dev/null +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageerrornotifierinterface.h @@ -0,0 +1,27 @@ +// 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 "projectstorageids.h" + +#include + +namespace QmlDesigner { + +class ProjectStorageErrorNotifierInterface +{ +public: + ProjectStorageErrorNotifierInterface() = default; + ProjectStorageErrorNotifierInterface(ProjectStorageErrorNotifierInterface &&) = default; + ProjectStorageErrorNotifierInterface &operator=(ProjectStorageErrorNotifierInterface &&) = default; + ProjectStorageErrorNotifierInterface(const ProjectStorageErrorNotifierInterface &) = delete; + ProjectStorageErrorNotifierInterface &operator=(const ProjectStorageErrorNotifierInterface &) = delete; + + virtual void typeNameCannotBeResolved(Utils::SmallStringView typeName, SourceId souceId) = 0; + +protected: + ~ProjectStorageErrorNotifierInterface() = default; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.cpp b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.cpp index a5dc60c4fa4..a86b78a785f 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.cpp @@ -148,32 +148,32 @@ const char *CannotParseQmlDocumentFile::what() const noexcept return "Cannot parse qml types file!"; } -ProjectDataHasInvalidProjectSourceId::ProjectDataHasInvalidProjectSourceId() +DirectoryInfoHasInvalidProjectSourceId::DirectoryInfoHasInvalidProjectSourceId() { - category().threadEvent("ProjectDataHasInvalidProjectSourceId"_t); + category().threadEvent("DirectoryInfoHasInvalidProjectSourceId"_t); } -const char *ProjectDataHasInvalidProjectSourceId::what() const noexcept +const char *DirectoryInfoHasInvalidProjectSourceId::what() const noexcept { return "The project source id is invalid!"; } -ProjectDataHasInvalidSourceId::ProjectDataHasInvalidSourceId() +DirectoryInfoHasInvalidSourceId::DirectoryInfoHasInvalidSourceId() { - category().threadEvent("ProjectDataHasInvalidSourceId"_t); + category().threadEvent("DirectoryInfoHasInvalidSourceId"_t); } -const char *ProjectDataHasInvalidSourceId::what() const noexcept +const char *DirectoryInfoHasInvalidSourceId::what() const noexcept { return "The source id is invalid!"; } -ProjectDataHasInvalidModuleId::ProjectDataHasInvalidModuleId() +DirectoryInfoHasInvalidModuleId::DirectoryInfoHasInvalidModuleId() { - category().threadEvent("ProjectDataHasInvalidModuleId"_t); + category().threadEvent("DirectoryInfoHasInvalidModuleId"_t); } -const char *ProjectDataHasInvalidModuleId::what() const noexcept +const char *DirectoryInfoHasInvalidModuleId::what() const noexcept { return "The module id is invalid!"; } diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.h index d85f1f7f9ee..f4f78f714bb 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageexceptions.h @@ -130,24 +130,24 @@ public: const char *what() const noexcept override; }; -class QMLDESIGNERCORE_EXPORT ProjectDataHasInvalidProjectSourceId : public ProjectStorageError +class QMLDESIGNERCORE_EXPORT DirectoryInfoHasInvalidProjectSourceId : public ProjectStorageError { public: - ProjectDataHasInvalidProjectSourceId(); + DirectoryInfoHasInvalidProjectSourceId(); const char *what() const noexcept override; }; -class QMLDESIGNERCORE_EXPORT ProjectDataHasInvalidSourceId : public ProjectStorageError +class QMLDESIGNERCORE_EXPORT DirectoryInfoHasInvalidSourceId : public ProjectStorageError { public: - ProjectDataHasInvalidSourceId(); + DirectoryInfoHasInvalidSourceId(); const char *what() const noexcept override; }; -class QMLDESIGNERCORE_EXPORT ProjectDataHasInvalidModuleId : public ProjectStorageError +class QMLDESIGNERCORE_EXPORT DirectoryInfoHasInvalidModuleId : public ProjectStorageError { public: - ProjectDataHasInvalidModuleId(); + DirectoryInfoHasInvalidModuleId(); const char *what() const noexcept override; }; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h index 9f0c134ed39..1d630344aec 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinfotypes.h @@ -7,6 +7,9 @@ #include #include +#include + +#include #include #include @@ -15,12 +18,8 @@ namespace QmlDesigner { -template -constexpr std::underlying_type_t to_underlying(Enumeration enumeration) noexcept -{ - static_assert(std::is_enum_v, "to_underlying expect an enumeration"); - return static_cast>(enumeration); -} +template +using SmallPathStrings = QVarLengthArray; enum class FlagIs : unsigned int { False, Set, True }; @@ -42,6 +41,34 @@ void convertToString(String &string, const FlagIs &flagIs) namespace QmlDesigner::Storage { +enum class ModuleKind { QmlLibrary, CppLibrary, PathLibrary }; + +struct Module +{ + Module() = default; + + Module(Utils::SmallStringView name, Storage::ModuleKind kind) + : name{name} + , kind{kind} + {} + + template + Module(const ModuleType &module) + : name{module.name} + , kind{module.kind} + {} + + Utils::PathString name; + Storage::ModuleKind kind = Storage::ModuleKind::QmlLibrary; + + friend bool operator==(const Module &first, const Module &second) + { + return first.name == second.name && first.kind == second.kind; + } + + explicit operator bool() const { return name.size(); } +}; + enum class PropertyDeclarationTraits : int { None = 0, IsReadOnly = 1 << 0, @@ -352,6 +379,7 @@ using ToolTipString = Utils::BasicSmallString<94>; struct ItemLibraryEntry { ItemLibraryEntry(TypeId typeId, + Utils::SmallStringView typeName, Utils::SmallStringView name, Utils::SmallStringView iconPath, Utils::SmallStringView category, @@ -359,6 +387,7 @@ struct ItemLibraryEntry Utils::SmallStringView toolTip, Utils::SmallStringView templatePath) : typeId{typeId} + , typeName{typeName} , name{name} , iconPath{iconPath} , category{category} @@ -368,6 +397,7 @@ struct ItemLibraryEntry {} ItemLibraryEntry(TypeId typeId, + Utils::SmallStringView typeName, Utils::SmallStringView name, Utils::SmallStringView iconPath, Utils::SmallStringView category, @@ -375,6 +405,7 @@ struct ItemLibraryEntry Utils::SmallStringView toolTip, ItemLibraryProperties properties) : typeId{typeId} + , typeName{typeName} , name{name} , iconPath{iconPath} , category{category} @@ -389,6 +420,7 @@ struct ItemLibraryEntry using NanotraceHR::dictonary; using NanotraceHR::keyValue; auto dict = dictonary(keyValue("type id", entry.typeId), + keyValue("type name", entry.typeName), keyValue("name", entry.name), keyValue("icon path", entry.iconPath), keyValue("category", entry.category), @@ -402,6 +434,7 @@ struct ItemLibraryEntry } TypeId typeId; + Utils::SmallString typeName; Utils::SmallString name; Utils::PathString iconPath; Utils::SmallString category; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h index 971e635517a..4d840d2a5c5 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageinterface.h @@ -31,8 +31,8 @@ public: virtual void addObserver(ProjectStorageObserver *observer) = 0; virtual void removeObserver(ProjectStorageObserver *observer) = 0; - virtual ModuleId moduleId(::Utils::SmallStringView name) const = 0; - virtual Utils::SmallString moduleName(ModuleId moduleId) const = 0; + virtual ModuleId moduleId(::Utils::SmallStringView name, Storage::ModuleKind kind) const = 0; + virtual QmlDesigner::Storage::Module module(ModuleId moduleId) const = 0; virtual std::optional propertyDeclaration(PropertyDeclarationId propertyDeclarationId) const = 0; virtual TypeId typeId(ModuleId moduleId, @@ -80,16 +80,20 @@ public: virtual bool isBasedOn(TypeId, TypeId, TypeId, TypeId, TypeId, TypeId, TypeId, TypeId) const = 0; virtual FileStatus fetchFileStatus(SourceId sourceId) const = 0; - virtual Storage::Synchronization::ProjectDatas fetchProjectDatas(SourceId sourceId) const = 0; - virtual std::optional fetchProjectData(SourceId sourceId) const = 0; + virtual Storage::Synchronization::DirectoryInfos fetchDirectoryInfos(SourceId sourceId) const = 0; + virtual Storage::Synchronization::DirectoryInfos fetchDirectoryInfos( + SourceId directorySourceId, Storage::Synchronization::FileType) const + = 0; + virtual std::optional fetchDirectoryInfo(SourceId sourceId) const = 0; + virtual SmallSourceIds<32> fetchSubdirectorySourceIds(SourceId directorySourceId) const = 0; virtual SourceId propertyEditorPathId(TypeId typeId) const = 0; virtual const Storage::Info::CommonTypeCache &commonTypeCache() const = 0; - template + template TypeId commonTypeId() const { - return commonTypeCache().template typeId(); + return commonTypeCache().template typeId(); } template @@ -108,7 +112,7 @@ protected: ProjectStorageInterface() = default; ~ProjectStorageInterface() = default; - virtual ModuleId fetchModuleIdUnguarded(Utils::SmallStringView name) const = 0; + virtual ModuleId fetchModuleIdUnguarded(Utils::SmallStringView name, Storage::ModuleKind moduleKind) const = 0; virtual TypeId fetchTypeIdByModuleIdAndExportedName(ModuleId moduleId, Utils::SmallStringView name) const = 0; }; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h index 8d810d94bd2..1592628af53 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -82,7 +83,7 @@ void convertToString(String &string, const TypeNameKind &kind) } } -enum class FileType : char { QmlTypes, QmlDocument }; +enum class FileType : char { QmlTypes, QmlDocument, Directory }; template void convertToString(String &string, const FileType &type) @@ -94,6 +95,9 @@ void convertToString(String &string, const FileType &type) case FileType::QmlDocument: convertToString(string, "QmlDocument"); break; + case FileType::Directory: + convertToString(string, "Directory"); + break; } } @@ -204,7 +208,7 @@ void convertToString(String &string, const IsAutoVersion &isAutoVersion) constexpr bool operator<(IsAutoVersion first, IsAutoVersion second) { - return to_underlying(first) < to_underlying(second); + return Utils::to_underlying(first) < Utils::to_underlying(second); } class ModuleExportedImport @@ -1160,44 +1164,44 @@ public: using PropertyEditorQmlPaths = std::vector; -class ProjectData +class DirectoryInfo { public: - ProjectData(SourceId projectSourceId, SourceId sourceId, ModuleId moduleId, FileType fileType) - : projectSourceId{projectSourceId} + DirectoryInfo(SourceId directorySourceId, SourceId sourceId, ModuleId moduleId, FileType fileType) + : directorySourceId{directorySourceId} , sourceId{sourceId} , moduleId{moduleId} , fileType{fileType} {} - friend bool operator==(const ProjectData &first, const ProjectData &second) + friend bool operator==(const DirectoryInfo &first, const DirectoryInfo &second) { - return first.projectSourceId == second.projectSourceId && first.sourceId == second.sourceId + return first.directorySourceId == second.directorySourceId && first.sourceId == second.sourceId && first.moduleId.internalId() == second.moduleId.internalId() && first.fileType == second.fileType; } template - friend void convertToString(String &string, const ProjectData &projectData) + friend void convertToString(String &string, const DirectoryInfo &directoryInfo) { using NanotraceHR::dictonary; using NanotraceHR::keyValue; - auto dict = dictonary(keyValue("project source id", projectData.projectSourceId), - keyValue("source id", projectData.sourceId), - keyValue("module id", projectData.moduleId), - keyValue("file type", projectData.fileType)); + auto dict = dictonary(keyValue("project source id", directoryInfo.directorySourceId), + keyValue("source id", directoryInfo.sourceId), + keyValue("module id", directoryInfo.moduleId), + keyValue("file type", directoryInfo.fileType)); convertToString(string, dict); } public: - SourceId projectSourceId; + SourceId directorySourceId; SourceId sourceId; ModuleId moduleId; FileType fileType; }; -using ProjectDatas = std::vector; +using DirectoryInfos = std::vector; class TypeAnnotation { @@ -1291,9 +1295,9 @@ public: , fileStatuses(std::move(fileStatuses)) {} - SynchronizationPackage(SourceIds updatedProjectSourceIds, ProjectDatas projectDatas) - : projectDatas(std::move(projectDatas)) - , updatedProjectSourceIds(std::move(updatedProjectSourceIds)) + SynchronizationPackage(SourceIds updatedDirectoryInfoSourceIds, DirectoryInfos directoryInfos) + : directoryInfos(std::move(directoryInfos)) + , updatedDirectoryInfoSourceIds(std::move(updatedDirectoryInfoSourceIds)) {} public: @@ -1302,8 +1306,8 @@ public: SourceIds updatedSourceIds; SourceIds updatedFileStatusSourceIds; FileStatuses fileStatuses; - ProjectDatas projectDatas; - SourceIds updatedProjectSourceIds; + DirectoryInfos directoryInfos; + SourceIds updatedDirectoryInfoSourceIds; Imports moduleDependencies; SourceIds updatedModuleDependencySourceIds; ModuleExportedImports moduleExportedImports; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp index 761d6371efe..a0e7bba3c52 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp @@ -13,9 +13,9 @@ #include "sourcepathcache.h" #include "typeannotationreader.h" -#include - #include +#include +#include #include #include @@ -102,6 +102,8 @@ ProjectStorageUpdater::Components createComponents( } for (const QmlDirParser::Component &qmlDirParserComponent : qmlDirParserComponents) { + if (qmlDirParserComponent.fileName.contains('/')) + continue; components.push_back(ProjectStorageUpdater::Component{qmlDirParserComponent.fileName, qmlDirParserComponent.typeName, moduleId, @@ -134,13 +136,13 @@ SourceIds filterNotUpdatedSourceIds(SourceIds updatedSourceIds, SourceIds notUpd } void addSourceIds(SourceIds &sourceIds, - const Storage::Synchronization::ProjectDatas &projectDatas, + const Storage::Synchronization::DirectoryInfos &directoryInfos, TracerLiteral message, Tracer &tracer) { - for (const auto &projectData : projectDatas) { - tracer.tick(message, keyValue("source id", projectData.sourceId)); - sourceIds.push_back(projectData.sourceId); + for (const auto &directoryInfo : directoryInfos) { + tracer.tick(message, keyValue("source id", directoryInfo.sourceId)); + sourceIds.push_back(directoryInfo.sourceId); } } @@ -164,8 +166,8 @@ void addDependencies(Storage::Imports &dependencies, Tracer &tracer) { for (const QmlDirParser::Import &qmldirDependency : qmldirDependencies) { - ModuleId moduleId = projectStorage.moduleId(Utils::PathString{qmldirDependency.module} - + "-cppnative"); + ModuleId moduleId = projectStorage.moduleId(Utils::PathString{qmldirDependency.module}, + Storage::ModuleKind::CppLibrary); auto &import = dependencies.emplace_back(moduleId, Storage::Version{}, sourceId); tracer.tick(message, keyValue("import", import)); } @@ -177,6 +179,7 @@ void addModuleExportedImport(Storage::Synchronization::ModuleExportedImports &im Storage::Version version, Storage::Synchronization::IsAutoVersion isAutoVersion, std::string_view moduleName, + Storage::ModuleKind moduleKind, std::string_view exportedModuleName) { NanotraceHR::Tracer tracer{"add module exported imports"_t, @@ -186,16 +189,21 @@ void addModuleExportedImport(Storage::Synchronization::ModuleExportedImports &im keyValue("version", version), keyValue("is auto version", isAutoVersion), keyValue("module name", moduleName), + keyValue("module kind", moduleKind), keyValue("exported module name", exportedModuleName)}; imports.emplace_back(moduleId, exportedModuleId, version, isAutoVersion); } +bool isOptionalImport(QmlDirParser::Import::Flags flags) +{ + return flags & QmlDirParser::Import::Optional && !(flags & QmlDirParser::Import::OptionalDefault); +} + void addModuleExportedImports(Storage::Synchronization::ModuleExportedImports &imports, ModuleId moduleId, ModuleId cppModuleId, std::string_view moduleName, - std::string_view cppModuleName, const QList &qmldirImports, ProjectStorageInterface &projectStorage) { @@ -205,24 +213,31 @@ void addModuleExportedImports(Storage::Synchronization::ModuleExportedImports &i keyValue("module id", moduleId)}; for (const QmlDirParser::Import &qmldirImport : qmldirImports) { + if (isOptionalImport(qmldirImport.flags)) + continue; + Utils::PathString exportedModuleName{qmldirImport.module}; - ModuleId exportedModuleId = projectStorage.moduleId(exportedModuleName); + using Storage::ModuleKind; + ModuleId exportedModuleId = projectStorage.moduleId(exportedModuleName, + ModuleKind::QmlLibrary); addModuleExportedImport(imports, moduleId, exportedModuleId, convertVersion(qmldirImport.version), convertToIsAutoVersion(qmldirImport.flags), moduleName, + ModuleKind::QmlLibrary, exportedModuleName); - exportedModuleName += "-cppnative"; - ModuleId exportedCppModuleId = projectStorage.moduleId(exportedModuleName); + ModuleId exportedCppModuleId = projectStorage.moduleId(exportedModuleName, + ModuleKind::CppLibrary); addModuleExportedImport(imports, cppModuleId, exportedCppModuleId, Storage::Version{}, Storage::Synchronization::IsAutoVersion::No, - cppModuleName, + moduleName, + ModuleKind::CppLibrary, exportedModuleName); } } @@ -272,6 +287,8 @@ void ProjectStorageUpdater::update(QStringList directories, try { m_projectStorage.synchronize(std::move(package)); + } catch (const TypeNameDoesNotExists &exception) { + qDebug() << "missing type: " << exception.what(); } catch (...) { qWarning() << "Project storage could not been updated!"; } @@ -289,7 +306,7 @@ void ProjectStorageUpdater::updateQmlTypes(const QStringList &qmlTypesPaths, NanotraceHR::Tracer tracer{"update qmltypes file"_t, category()}; - ModuleId moduleId = m_projectStorage.moduleId("QML-cppnative"); + ModuleId moduleId = m_projectStorage.moduleId("QML", Storage::ModuleKind::CppLibrary); for (const QString &qmlTypesPath : qmlTypesPaths) { SourceId sourceId = m_pathCache.sourceId(SourcePath{qmlTypesPath}); @@ -298,21 +315,19 @@ void ProjectStorageUpdater::updateQmlTypes(const QStringList &qmlTypesPaths, keyValue("source id", sourceId), keyValue("qml types path", qmlTypesPath)); - Storage::Synchronization::ProjectData projectData{sourceId, - sourceId, - moduleId, - Storage::Synchronization::FileType::QmlTypes}; + Storage::Synchronization::DirectoryInfo directoryInfo{ + sourceId, sourceId, moduleId, Storage::Synchronization::FileType::QmlTypes}; - FileState state = parseTypeInfo(projectData, + FileState state = parseTypeInfo(directoryInfo, Utils::PathString{qmlTypesPath}, package, notUpdatedSourceIds); if (state == FileState::Changed) { - tracer.tick("append project data"_t, keyValue("project data", projectData)); - package.projectDatas.push_back(std::move(projectData)); + tracer.tick("append project data"_t, keyValue("project data", directoryInfo)); + package.directoryInfos.push_back(std::move(directoryInfo)); tracer.tick("append updated project source ids"_t, keyValue("source id", sourceId)); - package.updatedProjectSourceIds.push_back(sourceId); + package.updatedDirectoryInfoSourceIds.push_back(sourceId); } } } @@ -352,11 +367,11 @@ void ProjectStorageUpdater::updateDirectoryChanged(std::string_view directoryPat package.updatedSourceIds.push_back(qmldirSourceId); } + using Storage::ModuleKind; Utils::PathString moduleName{parser.typeNamespace()}; - ModuleId moduleId = m_projectStorage.moduleId(moduleName); - Utils::PathString cppModuleName = moduleName + "-cppnative"; - ModuleId cppModuleId = m_projectStorage.moduleId(cppModuleName); - ModuleId pathModuleId = m_projectStorage.moduleId(directoryPath); + ModuleId moduleId = m_projectStorage.moduleId(moduleName, ModuleKind::QmlLibrary); + ModuleId cppModuleId = m_projectStorage.moduleId(moduleName, ModuleKind::CppLibrary); + ModuleId pathModuleId = m_projectStorage.moduleId(directoryPath, ModuleKind::PathLibrary); auto imports = filterMultipleEntries(parser.imports()); @@ -364,16 +379,15 @@ void ProjectStorageUpdater::updateDirectoryChanged(std::string_view directoryPat moduleId, cppModuleId, moduleName, - cppModuleName, imports, m_projectStorage); tracer.tick("append updated module id"_t, keyValue("module id", moduleId)); package.updatedModuleIds.push_back(moduleId); - const auto qmlProjectDatas = m_projectStorage.fetchProjectDatas(directorySourceId); - addSourceIds(package.updatedSourceIds, qmlProjectDatas, "append updated source id"_t, tracer); + const auto qmlDirectoryInfos = m_projectStorage.fetchDirectoryInfos(directorySourceId); + addSourceIds(package.updatedSourceIds, qmlDirectoryInfos, "append updated source id"_t, tracer); addSourceIds(package.updatedFileStatusSourceIds, - qmlProjectDatas, + qmlDirectoryInfos, "append updated file status source id"_t, tracer); @@ -399,7 +413,7 @@ void ProjectStorageUpdater::updateDirectoryChanged(std::string_view directoryPat watchedSourceIdsIds, qmldirState); tracer.tick("append updated project source id"_t, keyValue("module id", moduleId)); - package.updatedProjectSourceIds.push_back(directorySourceId); + package.updatedDirectoryInfoSourceIds.push_back(directorySourceId); } void ProjectStorageUpdater::updateDirectories(const QStringList &directories, @@ -410,10 +424,111 @@ void ProjectStorageUpdater::updateDirectories(const QStringList &directories, NanotraceHR::Tracer tracer{"update directories"_t, category()}; for (const QString &directory : directories) - updateDirectory({directory}, package, notUpdatedSourceIds, watchedSourceIdsIds); + updateDirectory({directory}, {}, package, notUpdatedSourceIds, watchedSourceIdsIds); +} + +void ProjectStorageUpdater::updateSubdirectories(const Utils::PathString &directoryPath, + SourceId directorySourceId, + FileState directoryState, + const SourceContextIds &subdirectoriesToIgnore, + Storage::Synchronization::SynchronizationPackage &package, + NotUpdatedSourceIds ¬UpdatedSourceIds, + WatchedSourceIdsIds &watchedSourceIdsIds) +{ + struct Directory + { + Directory(Utils::SmallStringView path, SourceContextId sourceContextId, SourceId sourceId) + : path{path} + , sourceContextId{sourceContextId} + , sourceId{sourceId} + {} + + bool operator<(const Directory &other) const + { + return sourceContextId < other.sourceContextId; + } + + bool operator==(const Directory &other) const + { + return sourceContextId == other.sourceContextId; + } + + Utils::PathString path; + SourceContextId sourceContextId; + SourceId sourceId; + }; + + struct Compare + { + bool operator()(const Directory &first, const Directory &second) const + { + return first.sourceContextId < second.sourceContextId; + } + + bool operator()(const Directory &first, SourceContextId second) const + { + return first.sourceContextId < second; + } + + bool operator()(SourceContextId first, const Directory &second) const + { + return first < second.sourceContextId; + } + }; + + using Directories = QVarLengthArray; + + auto subdirectorySourceIds = m_projectStorage.fetchSubdirectorySourceIds(directorySourceId); + auto subdirectories = Utils::transform( + subdirectorySourceIds, [&](SourceId sourceId) -> Directory { + auto sourceContextId = m_pathCache.sourceContextId(sourceId); + auto subdirectoryPath = m_pathCache.sourceContextPath(sourceContextId); + return {subdirectoryPath, sourceContextId, sourceId}; + }); + + auto exisitingSubdirectoryPaths = m_fileSystem.subdirectories(directoryPath.toQString()); + Directories existingSubdirecories; + for (const QString &subdirectory : exisitingSubdirectoryPaths) { + if (subdirectory.endsWith("/designer") || subdirectory.endsWith("/QtQuick/Scene2D") + || subdirectory.endsWith("/QtQuick/Scene3D")) + continue; + Utils::PathString subdirectoryPath = subdirectory; + auto [sourceContextId, sourceId] = m_pathCache.sourceContextAndSourceId( + SourcePath{subdirectoryPath + "/."}); + subdirectories.emplace_back(subdirectoryPath, sourceContextId, sourceId); + existingSubdirecories.emplace_back(subdirectoryPath, sourceContextId, sourceId); + } + + std::sort(subdirectories.begin(), subdirectories.end()); + subdirectories.erase(std::unique(subdirectories.begin(), subdirectories.end()), + subdirectories.end()); + + std::set_difference(subdirectories.begin(), + subdirectories.end(), + subdirectoriesToIgnore.begin(), + subdirectoriesToIgnore.end(), + Utils::make_iterator([&](const Directory &subdirectory) { + updateDirectory(subdirectory.path, + subdirectoriesToIgnore, + package, + notUpdatedSourceIds, + watchedSourceIdsIds); + }), + Compare{}); + + if (directoryState == FileState::Changed) { + for (const auto &[subdirectoryPath, sourceContextId, subdirectorySourceId] : + existingSubdirecories) { + package.directoryInfos.emplace_back(directorySourceId, + subdirectorySourceId, + ModuleId{}, + Storage::Synchronization::FileType::Directory); + } + } } void ProjectStorageUpdater::updateDirectory(const Utils::PathString &directoryPath, + const SourceContextIds &subdirectoriesToIgnore, Storage::Synchronization::SynchronizationPackage &package, NotUpdatedSourceIds ¬UpdatedSourceIds, WatchedSourceIdsIds &watchedSourceIdsIds) @@ -451,10 +566,10 @@ void ProjectStorageUpdater::updateDirectory(const Utils::PathString &directoryPa case FileState::NotChanged: { tracer.tick("update directory not changed"_t); - parseProjectDatas(m_projectStorage.fetchProjectDatas(directorySourceId), - package, - notUpdatedSourceIds, - watchedSourceIdsIds); + parseDirectoryInfos(m_projectStorage.fetchDirectoryInfos(directorySourceId), + package, + notUpdatedSourceIds, + watchedSourceIdsIds); break; } case FileState::NotExists: { @@ -462,21 +577,29 @@ void ProjectStorageUpdater::updateDirectory(const Utils::PathString &directoryPa package.updatedFileStatusSourceIds.push_back(directorySourceId); package.updatedFileStatusSourceIds.push_back(qmldirSourceId); - package.updatedProjectSourceIds.push_back(directorySourceId); + package.updatedDirectoryInfoSourceIds.push_back(directorySourceId); package.updatedSourceIds.push_back(qmldirSourceId); - auto qmlProjectDatas = m_projectStorage.fetchProjectDatas(directorySourceId); - for (const Storage::Synchronization::ProjectData &projectData : qmlProjectDatas) { - tracer.tick("append updated source id"_t, keyValue("source id", projectData.sourceId)); - package.updatedSourceIds.push_back(projectData.sourceId); + auto qmlDirectoryInfos = m_projectStorage.fetchDirectoryInfos(directorySourceId); + for (const Storage::Synchronization::DirectoryInfo &directoryInfo : qmlDirectoryInfos) { + tracer.tick("append updated source id"_t, keyValue("source id", directoryInfo.sourceId)); + package.updatedSourceIds.push_back(directoryInfo.sourceId); tracer.tick("append updated file status source id"_t, - keyValue("source id", projectData.sourceId)); - package.updatedFileStatusSourceIds.push_back(projectData.sourceId); + keyValue("source id", directoryInfo.sourceId)); + package.updatedFileStatusSourceIds.push_back(directoryInfo.sourceId); } break; } } + updateSubdirectories(directoryPath, + directorySourceId, + directoryState, + subdirectoriesToIgnore, + package, + notUpdatedSourceIds, + watchedSourceIdsIds); + tracer.end(keyValue("qmldir source path", qmldirSourcePath), keyValue("directory source path", directorySourcePath), keyValue("directory id", directoryId), @@ -509,8 +632,12 @@ void ProjectStorageUpdater::updatePropertyEditorPaths( auto state = fileState(directorySourceId, package, notUpdatedSourceIds); - if (state == FileState::Changed) - updatePropertyEditorPath(pathInfo.filePath(), package, directorySourceId); + if (state == FileState::Changed) { + updatePropertyEditorPath(pathInfo.filePath(), + package, + directorySourceId, + propertyEditorResourcesPath.size() + 1); + } } } @@ -647,7 +774,8 @@ void ProjectStorageUpdater::updateTypeAnnotation(const QString &directoryPath, void ProjectStorageUpdater::updatePropertyEditorPath( const QString &directoryPath, Storage::Synchronization::SynchronizationPackage &package, - SourceId directorySourceId) + SourceId directorySourceId, + long long pathOffset) { NanotraceHR::Tracer tracer{"update property editor path"_t, category(), @@ -660,28 +788,31 @@ void ProjectStorageUpdater::updatePropertyEditorPath( auto dir = QDir{directoryPath}; const auto fileInfos = dir.entryInfoList({"*Pane.qml", "*Specifics.qml"}, QDir::Files); for (const auto &fileInfo : fileInfos) - updatePropertyEditorFilePath(fileInfo.filePath(), package, directorySourceId); + updatePropertyEditorFilePath(fileInfo.filePath(), package, directorySourceId, pathOffset); } void ProjectStorageUpdater::updatePropertyEditorFilePath( const QString &path, Storage::Synchronization::SynchronizationPackage &package, - SourceId directorySourceId) + SourceId directorySourceId, + long long pathOffset) { NanotraceHR::Tracer tracer{"update property editor file path"_t, category(), keyValue("directory path", path), keyValue("directory source id", directorySourceId)}; - QRegularExpression regex{R"xo(.+\/(\w+)\/(\w+)(Specifics|Pane).qml)xo"}; - auto match = regex.match(path); + QRegularExpression regex{R"xo((.+)\/(\w+)(Specifics|Pane).qml)xo"}; + auto match = regex.match(QStringView{path}.mid(pathOffset)); QString oldModuleName; ModuleId moduleId; if (match.hasMatch()) { - auto moduleName = match.capturedView(1); + auto moduleName = match.capturedView(1).toString(); + moduleName.replace('/', '.'); if (oldModuleName != moduleName) { - oldModuleName = moduleName.toString(); - moduleId = m_projectStorage.moduleId(Utils::SmallString{moduleName}); + oldModuleName = moduleName; + moduleId = m_projectStorage.moduleId(Utils::SmallString{moduleName}, + Storage::ModuleKind::QmlLibrary); } Storage::TypeNameString typeName{match.capturedView(2)}; SourceId pathId = m_pathCache.sourceId(SourcePath{path}); @@ -770,7 +901,11 @@ void ProjectStorageUpdater::pathsWithIdsChanged(const std::vector &chan for (auto sourceContextId : directorySourceContextIds) { Utils::PathString directory = m_pathCache.sourceContextPath(sourceContextId); - updateDirectory(directory, package, notUpdatedSourceIds, watchedSourceIds); + updateDirectory(directory, + directorySourceContextIds, + package, + notUpdatedSourceIds, + watchedSourceIds); } for (SourceId sourceId : filterUniqueSourceIds(qmlDocumentSourceIds)) { @@ -782,9 +917,9 @@ void ProjectStorageUpdater::pathsWithIdsChanged(const std::vector &chan for (SourceId sourceId : filterUniqueSourceIds(std::move(qmltypesSourceIds))) { if (!contains(directorySourceContextIds, m_pathCache.sourceContextId(sourceId))) { auto qmltypesPath = m_pathCache.sourcePath(sourceId); - auto projectData = m_projectStorage.fetchProjectData(sourceId); - if (projectData) - parseTypeInfo(*projectData, qmltypesPath, package, notUpdatedSourceIds); + auto directoryInfo = m_projectStorage.fetchDirectoryInfo(sourceId); + if (directoryInfo) + parseTypeInfo(*directoryInfo, qmltypesPath, package, notUpdatedSourceIds); } } } catch (const QmlDesigner::CannotParseQmlTypesFile &) { @@ -849,41 +984,44 @@ void ProjectStorageUpdater::parseTypeInfos(const QStringList &typeInfos, tracer.tick("append module dependenct source source id"_t, keyValue("source id", sourceId)); package.updatedModuleDependencySourceIds.push_back(sourceId); - auto projectData = package.projectDatas.emplace_back( + const auto &directoryInfo = package.directoryInfos.emplace_back( directorySourceId, sourceId, moduleId, Storage::Synchronization::FileType::QmlTypes); tracer.tick("append project data"_t, keyValue("source id", sourceId)); - parseTypeInfo(projectData, qmltypesPath, package, notUpdatedSourceIds); + parseTypeInfo(directoryInfo, qmltypesPath, package, notUpdatedSourceIds); } } -void ProjectStorageUpdater::parseProjectDatas(const Storage::Synchronization::ProjectDatas &projectDatas, - Storage::Synchronization::SynchronizationPackage &package, - NotUpdatedSourceIds ¬UpdatedSourceIds, - WatchedSourceIdsIds &watchedSourceIds) +void ProjectStorageUpdater::parseDirectoryInfos( + const Storage::Synchronization::DirectoryInfos &directoryInfos, + Storage::Synchronization::SynchronizationPackage &package, + NotUpdatedSourceIds ¬UpdatedSourceIds, + WatchedSourceIdsIds &watchedSourceIds) { NanotraceHR::Tracer tracer{"parse project datas"_t, category()}; - for (const Storage::Synchronization::ProjectData &projectData : projectDatas) { - switch (projectData.fileType) { + for (const Storage::Synchronization::DirectoryInfo &directoryInfo : directoryInfos) { + switch (directoryInfo.fileType) { case Storage::Synchronization::FileType::QmlTypes: { - watchedSourceIds.qmltypesSourceIds.push_back(projectData.sourceId); + watchedSourceIds.qmltypesSourceIds.push_back(directoryInfo.sourceId); - auto qmltypesPath = m_pathCache.sourcePath(projectData.sourceId); - parseTypeInfo(projectData, qmltypesPath, package, notUpdatedSourceIds); + auto qmltypesPath = m_pathCache.sourcePath(directoryInfo.sourceId); + parseTypeInfo(directoryInfo, qmltypesPath, package, notUpdatedSourceIds); break; } case Storage::Synchronization::FileType::QmlDocument: { - watchedSourceIds.qmlSourceIds.push_back(projectData.sourceId); + watchedSourceIds.qmlSourceIds.push_back(directoryInfo.sourceId); - parseQmlComponent(projectData.sourceId, package, notUpdatedSourceIds); + parseQmlComponent(directoryInfo.sourceId, package, notUpdatedSourceIds); break; } + case Storage::Synchronization::FileType::Directory: + break; } } } -auto ProjectStorageUpdater::parseTypeInfo(const Storage::Synchronization::ProjectData &projectData, +auto ProjectStorageUpdater::parseTypeInfo(const Storage::Synchronization::DirectoryInfo &directoryInfo, Utils::SmallStringView qmltypesPath, Storage::Synchronization::SynchronizationPackage &package, NotUpdatedSourceIds ¬UpdatedSourceIds) -> FileState @@ -892,19 +1030,19 @@ auto ProjectStorageUpdater::parseTypeInfo(const Storage::Synchronization::Projec category(), keyValue("qmltypes path", qmltypesPath)}; - auto state = fileState(projectData.sourceId, package, notUpdatedSourceIds); + auto state = fileState(directoryInfo.sourceId, package, notUpdatedSourceIds); switch (state) { case FileState::Changed: { - tracer.tick("append updated source ids"_t, keyValue("source id", projectData.sourceId)); - package.updatedSourceIds.push_back(projectData.sourceId); + tracer.tick("append updated source ids"_t, keyValue("source id", directoryInfo.sourceId)); + package.updatedSourceIds.push_back(directoryInfo.sourceId); const auto content = m_fileSystem.contentAsQString(QString{qmltypesPath}); - m_qmlTypesParser.parse(content, package.imports, package.types, projectData); + m_qmlTypesParser.parse(content, package.imports, package.types, directoryInfo); break; } case FileState::NotChanged: { - tracer.tick("append not updated source ids"_t, keyValue("source id", projectData.sourceId)); - notUpdatedSourceIds.sourceIds.push_back(projectData.sourceId); + tracer.tick("append not updated source ids"_t, keyValue("source id", directoryInfo.sourceId)); + notUpdatedSourceIds.sourceIds.push_back(directoryInfo.sourceId); break; } case FileState::NotExists: @@ -951,9 +1089,9 @@ void ProjectStorageUpdater::parseQmlComponent(Utils::SmallStringView relativeFil tracer.tick("append not updated source id"_t, keyValue("source id", sourceId)); notUpdatedSourceIds.sourceIds.emplace_back(sourceId); - const auto &projectData = package.projectDatas.emplace_back( + const auto &directoryInfo = package.directoryInfos.emplace_back( directorySourceId, sourceId, ModuleId{}, Storage::Synchronization::FileType::QmlDocument); - tracer.tick("append project data"_t, keyValue("project data", projectData)); + tracer.tick("append project data"_t, keyValue("project data", directoryInfo)); return; } @@ -967,9 +1105,9 @@ void ProjectStorageUpdater::parseQmlComponent(Utils::SmallStringView relativeFil break; } - const auto &projectData = package.projectDatas.emplace_back( + const auto &directoryInfo = package.directoryInfos.emplace_back( directorySourceId, sourceId, ModuleId{}, Storage::Synchronization::FileType::QmlDocument); - tracer.tick("append project data"_t, keyValue("project data", projectData)); + tracer.tick("append project data"_t, keyValue("project data", directoryInfo)); tracer.tick("append updated source id"_t, keyValue("source id", sourceId)); package.updatedSourceIds.push_back(sourceId); diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h index 640969fe990..baecbd6b11b 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h @@ -143,9 +143,17 @@ private: WatchedSourceIdsIds &watchedSourceIdsIds); void updateDirectory(const Utils::PathString &directory, + const SourceContextIds &subdirecoriesToIgnore, Storage::Synchronization::SynchronizationPackage &package, NotUpdatedSourceIds ¬UpdatedSourceIds, WatchedSourceIdsIds &watchedSourceIdsIds); + void updateSubdirectories(const Utils::PathString &directory, + SourceId directorySourceId, + FileState directoryFileState, + const SourceContextIds &subdirecoriesToIgnore, + Storage::Synchronization::SynchronizationPackage &package, + NotUpdatedSourceIds ¬UpdatedSourceIds, + WatchedSourceIdsIds &watchedSourceIdsIds); void updateDirectoryChanged(std::string_view directoryPath, FileState qmldirState, SourcePath qmldirSourcePath, @@ -177,10 +185,12 @@ private: Storage::Synchronization::SynchronizationPackage &package); void updatePropertyEditorPath(const QString &path, Storage::Synchronization::SynchronizationPackage &package, - SourceId directorySourceId); + SourceId directorySourceId, + long long pathOffset); void updatePropertyEditorFilePath(const QString &filePath, Storage::Synchronization::SynchronizationPackage &package, - SourceId directorySourceId); + SourceId directorySourceId, + long long pathOffset); void parseTypeInfos(const QStringList &typeInfos, const QList &qmldirDependencies, const QList &qmldirImports, @@ -190,11 +200,11 @@ private: Storage::Synchronization::SynchronizationPackage &package, NotUpdatedSourceIds ¬UpdatedSourceIds, WatchedSourceIdsIds &watchedSourceIdsIds); - void parseProjectDatas(const Storage::Synchronization::ProjectDatas &projectDatas, - Storage::Synchronization::SynchronizationPackage &package, - NotUpdatedSourceIds ¬UpdatedSourceIds, - WatchedSourceIdsIds &watchedSourceIdsIds); - FileState parseTypeInfo(const Storage::Synchronization::ProjectData &projectData, + void parseDirectoryInfos(const Storage::Synchronization::DirectoryInfos &directoryInfos, + Storage::Synchronization::SynchronizationPackage &package, + NotUpdatedSourceIds ¬UpdatedSourceIds, + WatchedSourceIdsIds &watchedSourceIdsIds); + FileState parseTypeInfo(const Storage::Synchronization::DirectoryInfo &directoryInfo, Utils::SmallStringView qmltypesPath, Storage::Synchronization::SynchronizationPackage &package, NotUpdatedSourceIds ¬UpdatedSourceIds); diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp b/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp index 27efa8d530d..4338da62ce9 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/qmldocumentparser.cpp @@ -66,23 +66,32 @@ Storage::Import createImport(const QmlDom::Import &qmlImport, Utils::SmallStringView directoryPath, QmlDocumentParser::ProjectStorage &storage) { + using Storage::ModuleKind; using QmlUriKind = QQmlJS::Dom::QmlUri::Kind; auto &&uri = qmlImport.uri; - if (uri.kind() == QmlUriKind::RelativePath) { - auto path = createNormalizedPath(directoryPath, uri.localPath()); - auto moduleId = storage.moduleId(createNormalizedPath(directoryPath, uri.localPath())); - return Storage::Import(moduleId, Storage::Version{}, sourceId); - } - - if (uri.kind() == QmlUriKind::ModuleUri) { - auto moduleId = storage.moduleId(Utils::PathString{uri.moduleUri()}); + switch (uri.kind()) { + case QmlUriKind::AbsolutePath: + case QmlUriKind::DirectoryUrl: { + auto moduleId = storage.moduleId(Utils::PathString{uri.toString()}, ModuleKind::PathLibrary); return Storage::Import(moduleId, convertVersion(qmlImport.version), sourceId); } + case QmlUriKind::RelativePath: { + auto path = createNormalizedPath(directoryPath, uri.localPath()); + auto moduleId = storage.moduleId(createNormalizedPath(directoryPath, uri.localPath()), + ModuleKind::PathLibrary); + return Storage::Import(moduleId, Storage::Version{}, sourceId); + } + case QmlUriKind::ModuleUri: { + auto moduleId = storage.moduleId(Utils::PathString{uri.moduleUri()}, ModuleKind::QmlLibrary); + return Storage::Import(moduleId, convertVersion(qmlImport.version), sourceId); + } + case QmlUriKind::Invalid: + return Storage::Import{}; + } - auto moduleId = storage.moduleId(Utils::PathString{uri.toString()}); - return Storage::Import(moduleId, convertVersion(qmlImport.version), sourceId); + return Storage::Import{}; } QualifiedImports createQualifiedImports(const QList &qmlImports, @@ -122,11 +131,13 @@ void addImports(Storage::Imports &imports, } } - auto localDirectoryModuleId = storage.moduleId(directoryPath); + using Storage::ModuleKind; + + auto localDirectoryModuleId = storage.moduleId(directoryPath, ModuleKind::PathLibrary); imports.emplace_back(localDirectoryModuleId, Storage::Version{}, sourceId); ++importCount; - auto qmlModuleId = storage.moduleId("QML"); + auto qmlModuleId = storage.moduleId("QML", ModuleKind::QmlLibrary); imports.emplace_back(qmlModuleId, Storage::Version{}, sourceId); ++importCount; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp index 104338e514b..b3ec4f00242 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.cpp @@ -28,7 +28,7 @@ namespace QmlDesigner { constexpr auto category = ProjectStorageTracing::projectStorageUpdaterCategory; using NanotraceHR::keyValue; using Tracer = ProjectStorageTracing::Category::TracerType; - +using Storage::ModuleKind; namespace QmlDom = QQmlJS::Dom; namespace { @@ -71,8 +71,7 @@ const Storage::Import &appendImports(Storage::Imports &imports, }); Utils::PathString moduleName{QStringView(dependency.begin(), spaceFound)}; - moduleName.append("-cppnative"); - ModuleId cppModuleId = storage.moduleId(moduleName); + ModuleId cppModuleId = storage.moduleId(moduleName, ModuleKind::CppLibrary); return imports.emplace_back(cppModuleId, Storage::Version{}, sourceId); } @@ -98,7 +97,8 @@ void addImports(Storage::Imports &imports, const auto &import = imports.emplace_back(cppModuleId, Storage::Version{}, sourceId); tracer.tick("append import"_t, keyValue("import", import)); - if (ModuleId qmlCppModuleId = storage.moduleId("QML-cppnative"); cppModuleId != qmlCppModuleId) { + if (ModuleId qmlCppModuleId = storage.moduleId("QML", ModuleKind::CppLibrary); + cppModuleId != qmlCppModuleId) { const auto &import = imports.emplace_back(qmlCppModuleId, Storage::Version{}, sourceId); tracer.tick("append import"_t, keyValue("import", import)); } @@ -145,7 +145,8 @@ Storage::Synchronization::ExportedTypes createExports(const QList getSkipList(std::string_view moduleName) +Utils::span getSkipList(const Storage::Module &module) { static constexpr Utils::span emptySkipList; auto currentSkipList = emptySkipList; std::apply( [&](const auto &entry) { - if (entry.first == moduleName) + if (entry.first.first == module.name && entry.first.second == module.kind) currentSkipList = entry.second; }, skipLists); @@ -494,7 +495,7 @@ bool skipType(const QQmlJSExportedScope &object, Utils::span &objects, QmlTypesParser::ProjectStorage &storage, const ComponentWithoutNamespaces &componentNameWithoutNamespaces) @@ -502,15 +503,15 @@ void addTypes(Storage::Synchronization::Types &types, NanotraceHR::Tracer tracer{"add types"_t, category()}; types.reserve(Utils::usize(objects) + types.size()); - const auto skipList = getSkipList(storage.moduleName(projectData.moduleId)); + const auto skipList = getSkipList(storage.module(directoryInfo.moduleId)); for (const auto &object : objects) { if (skipType(object, skipList)) continue; addType(types, - projectData.sourceId, - projectData.moduleId, + directoryInfo.sourceId, + directoryInfo.moduleId, object, storage, componentNameWithoutNamespaces); @@ -522,7 +523,7 @@ void addTypes(Storage::Synchronization::Types &types, void QmlTypesParser::parse(const QString &sourceContent, Storage::Imports &imports, Storage::Synchronization::Types &types, - const Storage::Synchronization::ProjectData &projectData) + const Storage::Synchronization::DirectoryInfo &directoryInfo) { NanotraceHR::Tracer tracer{"qmltypes parser parse"_t, category()}; @@ -535,8 +536,8 @@ void QmlTypesParser::parse(const QString &sourceContent, auto componentNameWithoutNamespaces = createComponentNameWithoutNamespaces(components); - addImports(imports, projectData.sourceId, dependencies, m_storage, projectData.moduleId); - addTypes(types, projectData, components, m_storage, componentNameWithoutNamespaces); + addImports(imports, directoryInfo.sourceId, dependencies, m_storage, directoryInfo.moduleId); + addTypes(types, directoryInfo, components, m_storage, componentNameWithoutNamespaces); } #else @@ -544,7 +545,7 @@ void QmlTypesParser::parse(const QString &sourceContent, void QmlTypesParser::parse([[maybe_unused]] const QString &sourceContent, [[maybe_unused]] Storage::Imports &imports, [[maybe_unused]] Storage::Synchronization::Types &types, - [[maybe_unused]] const Storage::Synchronization::ProjectData &projectData) + [[maybe_unused]] const Storage::Synchronization::DirectoryInfo &directoryInfo) {} #endif diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.h b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.h index 4a6427501b3..c73a429f912 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparser.h @@ -31,7 +31,7 @@ public: void parse(const QString &sourceContent, Storage::Imports &imports, Storage::Synchronization::Types &types, - const Storage::Synchronization::ProjectData &projectData) override; + const Storage::Synchronization::DirectoryInfo &directoryInfo) override; private: #ifdef QDS_BUILD_QMLPARSER diff --git a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparserinterface.h b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparserinterface.h index cdc7cd54d7e..c0880cf5c64 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparserinterface.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/qmltypesparserinterface.h @@ -15,7 +15,7 @@ public: virtual void parse(const QString &sourceContent, Storage::Imports &imports, Storage::Synchronization::Types &types, - const Storage::Synchronization::ProjectData &projectData) + const Storage::Synchronization::DirectoryInfo &directoryInfo) = 0; protected: diff --git a/src/plugins/qmldesigner/designercore/projectstorage/sourcepath.h b/src/plugins/qmldesigner/designercore/projectstorage/sourcepath.h index b655c5cc345..fa550a4d52e 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/sourcepath.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/sourcepath.h @@ -7,6 +7,8 @@ #include +#include + namespace QmlDesigner { class SourcePath : public Utils::PathString @@ -128,5 +130,6 @@ private: }; using SourcePaths = std::vector; - +template +using SmallSourcePaths = QVarLengthArray; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/projectstorage/storagecache.h b/src/plugins/qmldesigner/designercore/projectstorage/storagecache.h index 85c6147d2c7..32ecb1c3f7e 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/storagecache.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/storagecache.h @@ -313,7 +313,7 @@ private: return entries.end(); } - auto value = *found; + const auto &value = *found; if (value == view) { return found; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/typeannotationreader.cpp b/src/plugins/qmldesigner/designercore/projectstorage/typeannotationreader.cpp index e283d849a47..71eba949662 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/typeannotationreader.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/typeannotationreader.cpp @@ -178,8 +178,15 @@ TypeAnnotationReader::ParserSate TypeAnnotationReader::readDocument(const QStrin TypeAnnotationReader::ParserSate TypeAnnotationReader::readMetaInfoRootElement(const QString &name) { if (name == typeElementName) { - m_typeAnnotations.emplace_back(m_sourceId, m_directorySourceId); + auto &annotation = m_typeAnnotations.emplace_back(m_sourceId, m_directorySourceId); + annotation.traits.canBeDroppedInFormEditor = FlagIs::True; + annotation.traits.canBeDroppedInNavigator = FlagIs::True; + annotation.traits.isMovable = FlagIs::True; + annotation.traits.isResizable = FlagIs::True; + annotation.traits.hasFormEditorItem = FlagIs::True; + annotation.traits.visibleInLibrary = FlagIs::True; m_itemLibraryEntries = json::array(); + return ParsingType; } else { addErrorInvalidType(name); @@ -258,7 +265,8 @@ void TypeAnnotationReader::readTypeProperty(QStringView name, const QVariant &va auto [moduleName, typeName] = decomposeTypePath(fullTypeName); m_typeAnnotations.back().typeName = typeName; - m_typeAnnotations.back().moduleId = m_projectStorage.moduleId(moduleName); + m_typeAnnotations.back().moduleId = m_projectStorage.moduleId(moduleName, + ModuleKind::QmlLibrary); } else if (name == "icon"_L1) { m_typeAnnotations.back().iconPath = absoluteFilePathForDocument(value.toString()); diff --git a/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.cpp b/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.cpp index b5798b713d2..cbe7b0ec384 100644 --- a/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.cpp +++ b/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.cpp @@ -88,4 +88,13 @@ Category &projectStorageUpdaterCategory() } // namespace ProjectStorageTracing +namespace MetaInfoTracing { +Category &category() +{ + thread_local Category category_{"meta info"_t, Tracing::eventQueueWithStringArguments(), category}; + + return category_; +} +} // namespace MetaInfoTracing + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.h b/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.h index 3a33834c708..899ceb6cd2c 100644 --- a/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.h +++ b/src/plugins/qmldesigner/designercore/tracing/qmldesignertracing.h @@ -62,4 +62,20 @@ using Category = NanotraceHR::StringViewWithStringArgumentsCategory; + +[[gnu::pure]] Category &category(); + +} // namespace MetaInfoTracing } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/uniquename.cpp b/src/plugins/qmldesigner/designercore/uniquename.cpp new file mode 100644 index 00000000000..d7506164dbe --- /dev/null +++ b/src/plugins/qmldesigner/designercore/uniquename.cpp @@ -0,0 +1,165 @@ +// 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 "uniquename.h" + +#include + +#include +#include + +namespace QmlDesigner::UniqueName { + +using namespace Qt::Literals; + +constexpr QLatin1StringView keywords[] { + "anchors"_L1, "as"_L1, "baseState"_L1, + "border"_L1, "bottom"_L1, "break"_L1, + "case"_L1, "catch"_L1, "clip"_L1, + "color"_L1, "continue"_L1, "data"_L1, + "debugger"_L1, "default"_L1, "delete"_L1, + "do"_L1, "else"_L1, "enabled"_L1, + "finally"_L1, "flow"_L1, "focus"_L1, + "font"_L1, "for"_L1, "function"_L1, + "height"_L1, "if"_L1, "import"_L1, + "in"_L1, "instanceof"_L1, "item"_L1, + "layer"_L1, "left"_L1, "margin"_L1, + "new"_L1, "opacity"_L1, "padding"_L1, + "parent"_L1, "print"_L1, "rect"_L1, + "return"_L1, "right"_L1, "scale"_L1, + "shaderInfo"_L1, "source"_L1, "sprite"_L1, + "spriteSequence"_L1, "state"_L1, "switch"_L1, + "text"_L1, "this"_L1, "throw"_L1, + "top"_L1, "try"_L1, "typeof"_L1, + "var"_L1, "visible"_L1, "void"_L1, + "while"_L1, "with"_L1, "x"_L1, + "y"_L1 +}; + +namespace { + +QString toCamelCase(const QString &input) +{ + QString result = input.at(0).toLower(); + bool capitalizeNext = false; + + for (const QChar &c : Utils::span{input}.subspan(1)) { + bool isValidChar = c.isLetterOrNumber() || c == '_'; + if (isValidChar) + result += capitalizeNext ? c.toUpper() : c; + + capitalizeNext = !isValidChar; + } + + return result; +} + +} // namespace + +/** + * @brief Generates a unique name based on the provided name. + * + * This method iteratively generates a name by appending suffixes until a unique name is found. + * The uniqueness of the generated name is determined by the provided predicate function. + * + * @param name The original name to be made unique. + * @param predicate A function that checks if a name exists. Returns true if the name exists, + * false if name is unique. + * @return A unique name derived from the provided name. + */ +QString generate(const QString &name, std::function predicate) +{ + if (!predicate(name)) + return name; + + // match prefix and number (including zero padding) parts + static QRegularExpression rgx("(\\D*?)(\\d+)$"); + QRegularExpressionMatch match = rgx.match(name); + + QString prefix; + int number = 0; + int padding = 0; + + if (match.hasMatch()) { + // Split the name into prefix and number + prefix = match.captured(1); + QString numberStr = match.captured(2); + number = numberStr.toInt(); + padding = numberStr.size(); + } else { + prefix = name; + } + + QString nameTemplate = "%1%2"; + QString newName; + do { + newName = nameTemplate.arg(prefix).arg(++number, padding, 10, QChar('0')); + } while (predicate(newName)); + + return newName; +} + +/** + * @brief Generates a unique path based on the provided path. If the path belongs to a file, the + * filename or if it's a directory, the directory name will be adjusted to ensure uniqueness. + * + * This method appends a numerical suffix (or increment it if it exists) to the filename or + * directory name if necessary to make it unique. + * + * @param path The original path to be made unique. + * @return A unique path derived from the provided path. + */ +QString generatePath(const QString &path) +{ + // Remove the trailing slash if it exists (otherwise QFileInfo::path() returns empty) + QString adjustedPath = path; + if (adjustedPath.endsWith('/')) + adjustedPath.chop(1); + + QFileInfo fileInfo = QFileInfo(adjustedPath); + QString baseName = fileInfo.baseName(); + QString suffix = fileInfo.completeSuffix(); + if (!suffix.isEmpty()) + suffix.prepend('.'); + + QString parentDir = fileInfo.path(); + QString pathTemplate = parentDir + "/%1" + suffix; + + QString uniqueBaseName = UniqueName::generate(baseName, [&] (const QString &currName) { + return QFileInfo::exists(pathTemplate.arg(currName)); + }); + + return pathTemplate.arg(uniqueBaseName); +} + +/** + * @brief Generates a unique ID based on the provided id + * + * This works similar to get() with additional restrictions: + * - Removes non-Latin1 characters + * - Removes spaces + * - Ensures the first letter is lowercase + * - Converts spaces to camel case + * - Prepends an underscore if id starts with a number or is a reserved word + * + * @param id The original id to be made unique. + * @return A unique Id (when predicate() returns false) + */ +QString generateId(const QString &id, std::function predicate) +{ + // remove non word (non A-Z, a-z, 0-9) or space characters + QString newId = id.trimmed(); + + newId = toCamelCase(newId); + + // prepend _ if starts with a digit or invalid id (such as reserved words) + if (newId.at(0).isDigit() || std::binary_search(std::begin(keywords), std::end(keywords), newId)) + newId.prepend('_'); + + if (!predicate) + return newId; + + return UniqueName::generate(newId, predicate); +} + +} // namespace QmlDesigner::UniqueName diff --git a/src/plugins/qmldesigner/designercore/uniquename.h b/src/plugins/qmldesigner/designercore/uniquename.h new file mode 100644 index 00000000000..85927c45147 --- /dev/null +++ b/src/plugins/qmldesigner/designercore/uniquename.h @@ -0,0 +1,17 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include + +#include + +namespace QmlDesigner::UniqueName { + +QString generate(const QString &name, std::function predicate); +QString generatePath(const QString &path); +QMLDESIGNERCORE_EXPORT QString generateId(const QString &id, + std::function predicate = {}); + +} // namespace QmlDesigner::UniqueName diff --git a/src/plugins/qmldesigner/designmodecontext.cpp b/src/plugins/qmldesigner/designmodecontext.cpp index 43fb0d9d756..796261935af 100644 --- a/src/plugins/qmldesigner/designmodecontext.cpp +++ b/src/plugins/qmldesigner/designmodecontext.cpp @@ -3,7 +3,6 @@ #include "designmodecontext.h" #include "assetslibrarywidget.h" -#include "collectionwidget.h" #include "designmodewidget.h" #include "edit3dwidget.h" #include "formeditorwidget.h" @@ -98,15 +97,4 @@ void TextEditorContext::contextHelp(const HelpCallback &callback) const qobject_cast(m_widget)->contextHelp(callback); } -CollectionEditorContext::CollectionEditorContext(QWidget *widget) - : IContext(widget) -{ - setWidget(widget); - setContext(Core::Context(Constants::C_QMLCOLLECTIONEDITOR, Constants::C_QT_QUICK_TOOLS_MENU)); -} - -void CollectionEditorContext::contextHelp(const HelpCallback &callback) const -{ - qobject_cast(m_widget)->contextHelp(callback); -} } // namespace QmlDesigner::Internal diff --git a/src/plugins/qmldesigner/designmodecontext.h b/src/plugins/qmldesigner/designmodecontext.h index 12f0113d977..1d146deb7d8 100644 --- a/src/plugins/qmldesigner/designmodecontext.h +++ b/src/plugins/qmldesigner/designmodecontext.h @@ -73,14 +73,5 @@ public: TextEditorContext(QWidget *widget); void contextHelp(const Core::IContext::HelpCallback &callback) const override; }; - -class CollectionEditorContext : public Core::IContext -{ - Q_OBJECT - -public: - CollectionEditorContext(QWidget *widget); - void contextHelp(const Core::IContext::HelpCallback &callback) const override; -}; } // namespace Internal } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designmodewidget.h b/src/plugins/qmldesigner/designmodewidget.h index 464994b7e34..881335da75c 100644 --- a/src/plugins/qmldesigner/designmodewidget.h +++ b/src/plugins/qmldesigner/designmodewidget.h @@ -11,7 +11,6 @@ #include #include -#include #include #include diff --git a/src/plugins/qmldesigner/qmldesignerconstants.h b/src/plugins/qmldesigner/qmldesignerconstants.h index fb691097f72..28f3ed6097b 100644 --- a/src/plugins/qmldesigner/qmldesignerconstants.h +++ b/src/plugins/qmldesigner/qmldesignerconstants.h @@ -19,7 +19,6 @@ 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 inline constexpr char C_QT_QUICK_TOOLS_MENU[] = "QmlDesigner::ToolsMenu"; @@ -51,6 +50,7 @@ inline constexpr char EDIT3D_EDIT_CAMERA[] = "QmlDesigner.Editor3D.EditCameraTog 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_SHOW_LOOKAT[] = "QmlDesigner.Editor3D.ToggleLookAt"; inline constexpr char EDIT3D_EDIT_SELECT_BACKGROUND_COLOR[] = "QmlDesigner.Editor3D.SelectBackgroundColor"; inline constexpr char EDIT3D_EDIT_SELECT_GRID_COLOR[] = "QmlDesigner.Editor3D.SelectGridColor"; @@ -78,8 +78,14 @@ 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_TYPE[] = "ComponentBundles"; -inline constexpr char GENERATED_COMPONENTS_FOLDER[] = "GeneratedComponents"; +inline constexpr char BUNDLE_JSON_FILENAME[] = "bundle.json"; +inline constexpr char COMPONENT_BUNDLES_TYPE[] = "Bundles"; +inline constexpr char COMPONENT_BUNDLES_MATERIAL_BUNDLE_TYPE[] = "Materials"; +inline constexpr char COMPONENT_BUNDLES_EFFECT_BUNDLE_TYPE[] = "Effects"; +inline constexpr char COMPONENT_BUNDLES_USER_MATERIAL_BUNDLE_TYPE[] = "UserMaterials"; +inline constexpr char COMPONENT_BUNDLES_USER_EFFECT_BUNDLE_TYPE[] = "UserEffects"; +inline constexpr char COMPONENT_BUNDLES_USER_3D_BUNDLE_TYPE[] = "User3D"; +inline constexpr char GENERATED_COMPONENTS_FOLDER[] = "Generated"; inline constexpr char COMPONENT_BUNDLES_ASSET_REF_FILE[] = "_asset_ref.json"; inline constexpr char OLD_QUICK_3D_ASSETS_FOLDER[] = "Quick3DAssets"; inline constexpr char QUICK_3D_COMPONENTS_FOLDER[] = "QtQuick3D"; @@ -90,7 +96,10 @@ inline constexpr char QUICK_3D_ASSET_IMPORT_DATA_SOURCE_KEY[] = "source_scene"; inline constexpr char OLD_ASSET_IMPORT_FOLDER[] = "asset_imports"; inline constexpr char OLD_EFFECTS_IMPORT_FOLDER[] = "/asset_imports/Effects"; inline constexpr char OLD_EFFECTS_FOLDER[] = "Effects"; -inline constexpr char COMPOSED_EFFECTS_TYPE[] = "ComposedEffects"; +inline constexpr char OLD_COMPONENT_BUNDLES_TYPE[] = "ComponentBundles"; +inline constexpr char OLD_COMPONENT_BUNDLES_MATERIAL_BUNDLE_TYPE[] = "MaterialBundle"; +inline constexpr char OLD_COMPONENT_BUNDLES_EFFECT_BUNDLE_TYPE[] = "EffectBundle"; +inline constexpr char COMPOSED_EFFECTS_TYPE[] = "Effects"; inline constexpr char MATERIAL_LIB_ID[] = "__materialLibrary__"; inline constexpr char MIME_TYPE_ITEM_LIBRARY_INFO[] @@ -98,7 +107,7 @@ inline constexpr char MIME_TYPE_ITEM_LIBRARY_INFO[] 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_ITEM[] = "application/vnd.qtdesignstudio.bundleitem"; 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"; @@ -178,7 +187,6 @@ inline constexpr char OBJECT_NAME_EFFECT_COMPOSER[] = "QQuickWidgetEffectCompose 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"; diff --git a/src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp b/src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp index 321d95197fc..97b2b46e7d5 100644 --- a/src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp +++ b/src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp @@ -56,6 +56,11 @@ QUrl ExternalDependencies::projectUrl() const return {}; } +QString ExternalDependencies::projectName() const +{ + return QmlDesignerPlugin::instance()->documentManager().currentProjectName(); +} + QString ExternalDependencies::currentProjectDirPath() const { return QmlDesignerPlugin::instance()->documentManager().currentProjectDirPath().toString(); diff --git a/src/plugins/qmldesigner/qmldesignerexternaldependencies.h b/src/plugins/qmldesigner/qmldesignerexternaldependencies.h index b4908c23833..6a49e4b5516 100644 --- a/src/plugins/qmldesigner/qmldesignerexternaldependencies.h +++ b/src/plugins/qmldesigner/qmldesignerexternaldependencies.h @@ -21,6 +21,7 @@ public: QString qmlPuppetFallbackDirectory() const override; QString defaultPuppetToplevelBuildDirectory() const override; QUrl projectUrl() const override; + QString projectName() const override; QString currentProjectDirPath() const override; QUrl currentResourcePath() const override; void parseItemLibraryDescriptions() override; diff --git a/src/plugins/qmldesigner/qmldesignerplugin.cpp b/src/plugins/qmldesigner/qmldesignerplugin.cpp index f1b8ecdfa77..9b6fb504250 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.cpp +++ b/src/plugins/qmldesigner/qmldesignerplugin.cpp @@ -4,7 +4,6 @@ #include "qmldesignerplugin.h" #include "qmldesignertr.h" -#include "collectioneditor/collectionview.h" #include "coreplugin/iwizardfactory.h" #include "designmodecontext.h" #include "designmodewidget.h" @@ -298,7 +297,6 @@ bool QmlDesignerPlugin::initialize(const QStringList & /*arguments*/, QString *e //TODO Move registering those types out of the property editor, since they are used also in the states editor Quick2PropertyEditorView::registerQmlTypes(); - CollectionView::registerDeclarativeType(); StudioQuickWidget::registerDeclarativeType(); QmlDesignerBase::WindowManager::registerDeclarativeType(); @@ -392,7 +390,6 @@ 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); @@ -400,7 +397,6 @@ 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/qmldesignerprojectmanager.cpp b/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp index 9602bf050fa..730a557d120 100644 --- a/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp +++ b/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp @@ -12,15 +12,16 @@ #include #include #include +#include #include #include #include #include #include -#include #include #include #include +#include #include #include @@ -181,7 +182,8 @@ public: pathCache.sourceId(SourcePath{project->projectDirectory().toString() + "/."}).internalId())} {} Sqlite::Database database; - ProjectStorage storage{database, database.isInitialized()}; + ProjectStorageErrorNotifier errorNotifier{pathCache}; + ProjectStorage storage{database, errorNotifier, database.isInitialized()}; PathCacheType pathCache{storage}; FileSystem fileSystem{pathCache}; FileStatusCache fileStatusCache{fileSystem}; @@ -238,30 +240,34 @@ QmlDesignerProjectManager::QmlDesignerProjectManager(ExternalDependenciesInterfa , m_externalDependencies{externalDependencies} { auto editorManager = ::Core::EditorManager::instance(); - QObject::connect(editorManager, &::Core::EditorManager::editorOpened, [&](auto *editor) { + QObject::connect(editorManager, &::Core::EditorManager::editorOpened, &dummy, [&](auto *editor) { editorOpened(editor); }); - QObject::connect(editorManager, &::Core::EditorManager::currentEditorChanged, [&](auto *editor) { - currentEditorChanged(editor); - }); - QObject::connect(editorManager, &::Core::EditorManager::editorsClosed, [&](const auto &editors) { - editorsClosed(editors); - }); + QObject::connect(editorManager, + &::Core::EditorManager::currentEditorChanged, + &dummy, + [&](auto *editor) { currentEditorChanged(editor); }); + QObject::connect(editorManager, + &::Core::EditorManager::editorsClosed, + &dummy, + [&](const auto &editors) { editorsClosed(editors); }); auto sessionManager = ::ProjectExplorer::ProjectManager::instance(); QObject::connect(sessionManager, &::ProjectExplorer::ProjectManager::projectAdded, + &dummy, [&](auto *project) { projectAdded(project); }); QObject::connect(sessionManager, &::ProjectExplorer::ProjectManager::aboutToRemoveProject, + &dummy, [&](auto *project) { aboutToRemoveProject(project); }); QObject::connect(sessionManager, &::ProjectExplorer::ProjectManager::projectRemoved, + &dummy, [&](auto *project) { projectRemoved(project); }); - QObject::connect(&m_previewImageCacheData->timer, - &QTimer::timeout, - this, - &QmlDesignerProjectManager::generatePreview); + QObject::connect(&m_previewImageCacheData->timer, &QTimer::timeout, &dummy, [&]() { + generatePreview(); + }); } QmlDesignerProjectManager::~QmlDesignerProjectManager() = default; @@ -337,81 +343,32 @@ Utils::FilePath qmlPath(::ProjectExplorer::Target *target) return {}; } -template -bool skipDirectoriesWith(const QStringView directoryPath, const Path &...paths) -{ - return (directoryPath.contains(paths) || ...); -} - -template -bool skipDirectoriesEndsWith(const QStringView directoryPath, const Path &...paths) -{ - return (directoryPath.endsWith(paths) || ...); -} - -bool skipPath(const QString &directoryPath) -{ - return skipDirectoriesWith(directoryPath, - u"QtApplicationManager", - u"QtInterfaceFramework", - u"QtOpcUa", - u"Qt3D", - u"Scene2D", - u"Scene3D", - u"QtWayland", - u"Qt5Compat", - u"QtCharts", - u"QtLocation", - u"QtPositioning", - u"MaterialEditor", - u"QtTextToSpeech", - u"QtWebEngine", - u"Qt/labs", - u"QtDataVisualization") - || skipDirectoriesEndsWith(directoryPath, u"designer"); -} - -void collectQmldirPaths(const QString &path, QStringList &qmldirPaths) -{ - QDirIterator dirIterator{path, QDir::Dirs | QDir::NoDotAndDotDot, QDirIterator::Subdirectories}; - - QString rootQmldirPath = path + "/qmldir"; - if (!skipPath(path) && QFileInfo::exists(rootQmldirPath)) - qmldirPaths.push_back(path); - - while (dirIterator.hasNext()) { - auto directoryPath = dirIterator.next(); - - QString qmldirPath = directoryPath + "/qmldir"; - if (!skipPath(directoryPath) && QFileInfo::exists(qmldirPath)) - qmldirPaths.push_back(directoryPath); - } -} - [[maybe_unused]] void projectQmldirPaths(::ProjectExplorer::Target *target, QStringList &qmldirPaths) { ::QmlProjectManager::QmlBuildSystem *buildSystem = getQmlBuildSystem(target); const Utils::FilePath projectDirectoryPath = buildSystem->canonicalProjectDir(); - const QStringList importPaths = buildSystem->importPaths(); - const QDir projectDirectory(projectDirectoryPath.toString()); - for (const QString &importPath : importPaths) - collectQmldirPaths(importPath, qmldirPaths); + qmldirPaths.push_back(projectDirectoryPath.path()); } [[maybe_unused]] void qtQmldirPaths(::ProjectExplorer::Target *target, QStringList &qmldirPaths) { - if constexpr (useProjectStorage()) - collectQmldirPaths(qmlPath(target).toString(), qmldirPaths); + if constexpr (useProjectStorage()) { + auto qmlRootPath = qmlPath(target).toString(); + qmldirPaths.push_back(qmlRootPath + "/QtQml"); + qmldirPaths.push_back(qmlRootPath + "/QtQuick"); + qmldirPaths.push_back(qmlRootPath + "/QtQuick3D"); + qmldirPaths.push_back(qmlRootPath + "/Qt5Compat"); + } } [[maybe_unused]] void qtQmldirPathsForLiteDesigner(QStringList &qmldirPaths) { if constexpr (useProjectStorage()) { auto qmlRootPath = QLibraryInfo::path(QLibraryInfo::QmlImportsPath); - collectQmldirPaths(qmlRootPath + "/QtQml", qmldirPaths); - collectQmldirPaths(qmlRootPath + "/QtQuick", qmldirPaths); + qmldirPaths.push_back(qmlRootPath + "/QtQml"); + qmldirPaths.push_back(qmlRootPath + "/QtQuick"); } } @@ -567,12 +524,12 @@ QmlDesignerProjectManager::ImageCacheData *QmlDesignerProjectManager::imageCache m_imageCacheData->nodeInstanceCollector.setTarget(project->activeTarget()); QObject::connect(project, &ProjectExplorer::Project::activeTargetChanged, - this, + &dummy, setTargetInImageCache); } QObject::connect(ProjectExplorer::ProjectManager::instance(), &ProjectExplorer::ProjectManager::startupProjectChanged, - this, + &dummy, [=](ProjectExplorer::Project *project) { setTargetInImageCache(activeTarget(project)); }); diff --git a/src/plugins/qmldesigner/qmldesignerprojectmanager.h b/src/plugins/qmldesigner/qmldesignerprojectmanager.h index bd45bf16c9a..1e5cec2e3e3 100644 --- a/src/plugins/qmldesigner/qmldesignerprojectmanager.h +++ b/src/plugins/qmldesigner/qmldesignerprojectmanager.h @@ -28,10 +28,8 @@ namespace QmlDesigner { class ExternalDependenciesInterface; -class QmlDesignerProjectManager : public QObject +class QmlDesignerProjectManager { - Q_OBJECT - class QmlDesignerProjectManagerProjectData; class PreviewImageCacheData; class ImageCacheData; @@ -70,5 +68,6 @@ private: std::unique_ptr m_previewImageCacheData; std::unique_ptr m_projectData; ExternalDependenciesInterface &m_externalDependencies; + QObject dummy; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/qtquickplugin/images/frame-animation-16px.png b/src/plugins/qmldesigner/qtquickplugin/images/frame-animation-16px.png new file mode 100644 index 00000000000..5171fdd79d9 Binary files /dev/null and b/src/plugins/qmldesigner/qtquickplugin/images/frame-animation-16px.png differ diff --git a/src/plugins/qmldesigner/qtquickplugin/images/frame-animation-24px.png b/src/plugins/qmldesigner/qtquickplugin/images/frame-animation-24px.png new file mode 100644 index 00000000000..2fba147700b Binary files /dev/null and b/src/plugins/qmldesigner/qtquickplugin/images/frame-animation-24px.png differ diff --git a/src/plugins/qmldesigner/qtquickplugin/images/frame-animation-24px@2x.png b/src/plugins/qmldesigner/qtquickplugin/images/frame-animation-24px@2x.png new file mode 100644 index 00000000000..170c113bbd1 Binary files /dev/null and b/src/plugins/qmldesigner/qtquickplugin/images/frame-animation-24px@2x.png differ diff --git a/src/plugins/qmldesigner/qtquickplugin/qtquickplugin.qrc b/src/plugins/qmldesigner/qtquickplugin/qtquickplugin.qrc index 34838797d49..c2a1ff59295 100644 --- a/src/plugins/qmldesigner/qtquickplugin/qtquickplugin.qrc +++ b/src/plugins/qmldesigner/qtquickplugin/qtquickplugin.qrc @@ -127,5 +127,8 @@ images/extended-view3d-16px.png images/extended-view3d-24px.png images/extended-view3d-24px@2x.png + images/frame-animation-16px.png + images/frame-animation-24px.png + images/frame-animation-24px@2x.png diff --git a/src/plugins/qmldesigner/qtquickplugin/quick.metainfo b/src/plugins/qmldesigner/qtquickplugin/quick.metainfo index 46cb42b60d2..63d390dded1 100644 --- a/src/plugins/qmldesigner/qtquickplugin/quick.metainfo +++ b/src/plugins/qmldesigner/qtquickplugin/quick.metainfo @@ -548,6 +548,26 @@ MetaInfo { } } + Type { + name: "QtQuick.FrameAnimation" + icon: ":/qtquickplugin/images/frame-animation-16px.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeContainer: false + } + + ItemLibraryEntry { + name: "Frame Animation" + category: "d.Qt Quick - Animation" + libraryIcon: ":/qtquickplugin/images/frame-animation-24px.png" + version: "2.0" + toolTip: qsTr("Triggers a handler at every animation frame update.") + } + } + Type { name: "QtQml.Timer" icon: ":/qtquickplugin/images/timer-16px.png" diff --git a/src/plugins/qmldesigner/settingspage.cpp b/src/plugins/qmldesigner/settingspage.cpp index 17c75a02853..df17f005a21 100644 --- a/src/plugins/qmldesigner/settingspage.cpp +++ b/src/plugins/qmldesigner/settingspage.cpp @@ -39,7 +39,8 @@ namespace Internal { static QStringList puppetModes() { - static QStringList puppetModeList{"", "all", "editormode", "rendermode", "previewmode", "bakelightsmode"}; + static QStringList puppetModeList{"", "all", "editormode", "rendermode", "previewmode", + "bakelightsmode", "import3dmode"}; return puppetModeList; } diff --git a/src/plugins/qmldesigner/shortcutmanager.cpp b/src/plugins/qmldesigner/shortcutmanager.cpp index 833466a2aa2..661ff3f271f 100644 --- a/src/plugins/qmldesigner/shortcutmanager.cpp +++ b/src/plugins/qmldesigner/shortcutmanager.cpp @@ -232,12 +232,10 @@ 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 - || isCollectionEditorActive); + m_deleteAction.setEnabled(isMatBrowserActive || isAssetsLibraryActive); m_cutAction.setEnabled(false); m_copyAction.setEnabled(false); m_pasteAction.setEnabled(false); @@ -294,8 +292,6 @@ void ShortCutManager::deleteSelected() 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(); } diff --git a/src/plugins/qmldesigner/shortcutmanager.h b/src/plugins/qmldesigner/shortcutmanager.h index 8714bb5fbcd..70b019217ca 100644 --- a/src/plugins/qmldesigner/shortcutmanager.h +++ b/src/plugins/qmldesigner/shortcutmanager.h @@ -64,7 +64,6 @@ private: bool isMatBrowserActive = false; bool isAssetsLibraryActive = false; - bool isCollectionEditorActive = false; }; } // namespace QmlDesigner diff --git a/src/plugins/qmlprojectmanager/.clang-format b/src/plugins/qmlprojectmanager/.clang-format new file mode 100644 index 00000000000..366f82f76f2 --- /dev/null +++ b/src/plugins/qmlprojectmanager/.clang-format @@ -0,0 +1,50 @@ +Language: Cpp +AccessModifierOffset: -4 +AlignEscapedNewlines: DontAlign +AllowShortFunctionsOnASingleLine: Inline +AlwaysBreakTemplateDeclarations: true # use with clang 19 +BinPackArguments: false +BinPackParameters: false +BraceWrapping: + AfterClass: true + AfterFunction: true + AfterStruct: true + SplitEmptyFunction: false + SplitEmptyRecord: false + SplitEmptyNamespace: false +BreakBeforeBinaryOperators: All +BreakBeforeBraces: Custom +BreakConstructorInitializers: BeforeComma +BreakInheritanceList: AfterComma +# BreakTemplateDeclarations: Yes # use with clang 19 +ColumnLimit: 100 +IncludeCategories: + - Regex: 'Q.*' + Priority: 8 + CaseSensitive: true +IndentPPDirectives: AfterHash +IndentWidth: 4 +KeepEmptyLinesAtTheStartOfBlocks: false +# Do not add QT_BEGIN_NAMESPACE/QT_END_NAMESPACE as this will indent lines in between. +ObjCBlockIndentWidth: 4 +PPIndentWidth: 2 +PackConstructorInitializers: Never +PenaltyBreakAssignment: 500 +PenaltyBreakBeforeFirstCallParameter: 150 +PenaltyBreakComment: 500 +PenaltyBreakFirstLessLess: 400 +PenaltyBreakString: 600 +PenaltyExcessCharacter: 7 +PenaltyReturnTypeOnItsOwnLine: 300 +QualifierAlignment: Custom +QualifierOrder: ['friend', 'inline', 'static', 'constexpr', 'const', 'type'] +ReferenceAlignment: Right +ReflowComments: false +SeparateDefinitionBlocks: Always +SortUsingDeclarations: Lexicographic +SpaceAfterCStyleCast: true +SpaceAfterTemplateKeyword: false +SpaceBeforeParens: ControlStatementsExceptControlMacros +SpacesInContainerLiterals: false +StatementAttributeLikeMacros: [emit] +TabWidth: 4 diff --git a/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp b/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp index 95cc859af52..82ba1fc2e41 100644 --- a/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp +++ b/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp @@ -14,7 +14,12 @@ const static QStringList imageFilesFilter{QStringLiteral("*.jpeg"), QStringLiteral("*.png"), QStringLiteral("*.svg"), QStringLiteral("*.hdr"), - QStringLiteral("*.ktx")}; + QStringLiteral("*.ktx"), + QStringLiteral("*.bmp"), + QStringLiteral("*.ttf"), + QStringLiteral("*.tiff"), + QStringLiteral("*.webp"), + QStringLiteral("*.gif")}; QString jsonValueToString(const QJsonValue &val, int indentationLevel, bool indented); diff --git a/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.cpp b/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.cpp index eb6e6de177a..5229d486ce3 100644 --- a/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.cpp +++ b/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.cpp @@ -79,7 +79,7 @@ void QmlProjectItem::setupFileFilters() connect(fileFilterItem.get(), &FileFilterItem::filesChanged, this, - &QmlProjectItem::qmlFilesChanged); + &QmlProjectItem::filesChanged); #endif m_content.push_back(std::move(fileFilterItem)); }; @@ -105,10 +105,7 @@ void QmlProjectItem::setupFileFilters() fileFilterItem->setDefaultDirectory(m_projectFile.parentDir().toString()); fileFilterItem->setDirectory(groupDir.toString()); #ifndef TESTS_ENABLED_QMLPROJECTITEM - connect(fileFilterItem.get(), - &FileFilterItem::filesChanged, - this, - &QmlProjectItem::qmlFilesChanged); + connect(fileFilterItem.get(), &FileFilterItem::filesChanged, this, &QmlProjectItem::filesChanged); #endif m_content.push_back(std::move(fileFilterItem)); }; diff --git a/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.h b/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.h index 7d57ad2e60d..5d0b520f144 100644 --- a/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.h +++ b/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.h @@ -92,7 +92,7 @@ public: void setEnableCMakeGeneration(bool enable); signals: - void qmlFilesChanged(const QSet &, const QSet &); + void filesChanged(const QSet &, const QSet &); private: typedef QSharedPointer ShrdPtrQPI; diff --git a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp index 00e501d6f88..296c3218369 100644 --- a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp +++ b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp @@ -32,14 +32,16 @@ #include #include -#include "projectexplorer/projectmanager.h" -#include "projectitem/qmlprojectitem.h" -#include "projectnode/qmlprojectnodes.h" +#include +#include +#include -#include "utils/algorithm.h" -#include "utils/qtcassert.h" +#include +#include +#include +#include -#include "texteditor/textdocument.h" +#include #include @@ -141,8 +143,6 @@ void QmlBuildSystem::registerMenuButtons() //wip: bool QmlBuildSystem::updateProjectFile() { - qDebug() << "debug#1-mainfilepath" << mainFilePath(); - QFile file(mainFilePath().fileName().append("project-test")); if (!file.open(QIODevice::ReadWrite | QIODevice::Truncate)) { qCritical() << "Cannot open Qml Project file for editing!"; @@ -220,17 +220,56 @@ void QmlBuildSystem::refresh(RefreshOptions options) void QmlBuildSystem::initProjectItem() { m_projectItem.reset(new QmlProjectItem{projectFilePath()}); - connect(m_projectItem.get(), - &QmlProjectItem::qmlFilesChanged, - this, - &QmlBuildSystem::refreshFiles); - connect(m_projectItem.get(), - &QmlProjectItem::qmlFilesChanged, + connect(m_projectItem.data(), &QmlProjectItem::filesChanged, this, &QmlBuildSystem::refreshFiles); + connect(m_projectItem.data(), + &QmlProjectItem::filesChanged, m_cmakeGen, &GenerateCmake::CMakeGenerator::update); m_cmakeGen->setEnabled(m_projectItem->enableCMakeGeneration()); + + initMcuProjectItems(); +} + +void QmlBuildSystem::initMcuProjectItems() +{ + m_mcuProjectItems.clear(); + m_mcuProjectFilesWatcher.clear(); + + Utils::FilePath projectDir = projectFilePath().parentDir(); + // traverse the project dir and find all other mcu projects (.qmlproject files) in the project tree + // and add them to the m_mcuProjectItems vector + QDirIterator it(projectDir.toFSPathString(), QDir::Files, QDirIterator::Subdirectories); + while (it.hasNext()) { + it.next(); + if (it.fileInfo().suffix() == "qmlproject" && it.filePath() != projectFilePath().toString()) { + auto qmlProjectItem = QSharedPointer( + new QmlProjectItem{Utils::FilePath::fromString(it.filePath())}); + + m_mcuProjectItems.append(qmlProjectItem); + connect(qmlProjectItem.data(), + &QmlProjectItem::filesChanged, + this, + &QmlBuildSystem::refreshFiles); + connect(qmlProjectItem.data(), + &QmlProjectItem::filesChanged, + m_cmakeGen, + &GenerateCmake::CMakeGenerator::update); + + m_mcuProjectFilesWatcher.addFile(it.filePath(), + Utils::FileSystemWatcher::WatchModifiedDate); + + connect(&m_mcuProjectFilesWatcher, + &Utils::FileSystemWatcher::fileChanged, + this, + [this](const QString &file) { + Q_UNUSED(file) + initMcuProjectItems(); + refresh(RefreshOptions::Files); + }); + } + } } void QmlBuildSystem::parseProjectFiles() @@ -239,7 +278,6 @@ void QmlBuildSystem::parseProjectFiles() modelManager->updateSourceFiles(m_projectItem->files(), true); } - const QString mainFileName = m_projectItem->mainFile(); if (!mainFileName.isEmpty()) { Utils::FilePath mainFilePath = canonicalProjectDir().resolvePath(mainFileName); @@ -265,6 +303,16 @@ void QmlBuildSystem::generateProjectTree() : FileNode::fileTypeForFileName(file); newRoot->addNestedNode(std::make_unique(file, fileType)); } + + for (const auto &mcuProjectItem : m_mcuProjectItems) { + for (const auto &file : mcuProjectItem->files()) { + // newRoot->addNestedNode(std::make_unique(file, FileType::Project)); + const FileType fileType = (file == projectFilePath()) + ? FileType::Project + : FileNode::fileTypeForFileName(file); + newRoot->addNestedNode(std::make_unique(file, fileType)); + } + } newRoot->addNestedNode(std::make_unique(projectFilePath(), FileType::Project)); setRootProjectNode(std::move(newRoot)); @@ -534,7 +582,7 @@ void QmlBuildSystem::refreshFiles(const QSet & /*added*/, const QSetenvironment(); } -QStringList QmlBuildSystem::customImportPaths() const -{ - return m_projectItem->importPaths(); -} - -QStringList QmlBuildSystem::customFileSelectors() const +QStringList QmlBuildSystem::fileSelectors() const { return m_projectItem->fileSelectors(); } @@ -682,7 +723,7 @@ QStringList QmlBuildSystem::importPaths() const return m_projectItem->importPaths(); } -QStringList QmlBuildSystem::absoluteImportPaths() +QStringList QmlBuildSystem::absoluteImportPaths() const { return Utils::transform(m_projectItem->importPaths(), [&](const QString &importPath) { Utils::FilePath filePath = Utils::FilePath::fromString(importPath); @@ -692,11 +733,6 @@ QStringList QmlBuildSystem::absoluteImportPaths() }); } -Utils::FilePaths QmlBuildSystem::files() const -{ - return m_projectItem->files(); -} - QString QmlBuildSystem::versionQt() const { return m_projectItem->versionQt(); diff --git a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.h b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.h index e3f9b99adb0..d91f60cdd12 100644 --- a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.h +++ b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.h @@ -6,7 +6,9 @@ #pragma once #include "../qmlprojectmanager_global.h" + #include +#include #include "qmlprojectmanager/cmakegen/cmakegenerator.h" @@ -72,9 +74,8 @@ public: Utils::EnvironmentItems environment() const; QStringList importPaths() const; - QStringList absoluteImportPaths(); - QStringList customImportPaths() const; - QStringList customFileSelectors() const; + QStringList absoluteImportPaths() const; + QStringList fileSelectors() const; bool multilanguageSupport() const; QStringList supportedLanguages() const; @@ -91,7 +92,6 @@ public: QStringList shaderToolArgs() const; QStringList shaderToolFiles() const; - Utils::FilePaths files() const; QString versionQt() const; QString versionQtQuick() const; @@ -117,10 +117,15 @@ private: const Utils::FilePath &mainFilePath, const QString &oldFile); + // this is the main project item QSharedPointer m_projectItem; + // these are the mcu project items which can be found in the project tree + QList> m_mcuProjectItems; + Utils::FileSystemWatcher m_mcuProjectFilesWatcher; bool m_blockFilesUpdate = false; void initProjectItem(); + void initMcuProjectItems(); void parseProjectFiles(); void generateProjectTree(); diff --git a/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.cpp b/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.cpp index 659c544ebd8..9ae576ec35a 100644 --- a/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.cpp +++ b/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.cpp @@ -17,8 +17,9 @@ #include "coreplugin/actionmanager/actioncontainer.h" #include -#include +#include #include +#include #include @@ -87,8 +88,8 @@ void CMakeGenerator::updateMenuAction() CMakeGenerator::CMakeGenerator(QmlBuildSystem *bs, QObject *parent) : QObject(parent) - , m_root(std::make_shared()) , m_buildSystem(bs) + , m_root(std::make_shared()) {} const QmlProject *CMakeGenerator::qmlProject() const @@ -170,6 +171,9 @@ void CMakeGenerator::update(const QSet &added, const QSet &rem std::set dirtyModules; for (const QString &add : added) { const Utils::FilePath path = Utils::FilePath::fromString(add); + if (ignore(path.parentDir())) + continue; + if (auto node = findOrCreateNode(m_root, path.parentDir())) { insertFile(node, path); if (auto module = findModuleFor(node)) @@ -208,10 +212,28 @@ bool CMakeGenerator::isResource(const Utils::FilePath &path) const return suffixes.contains(path.suffix(), Qt::CaseInsensitive); } -bool CMakeGenerator::ignoreFile(const Utils::FilePath &path) const +bool CMakeGenerator::ignore(const Utils::FilePath &path) const { - static const QStringList suffixes = { "hints" }; - return suffixes.contains(path.suffix(), Qt::CaseInsensitive); + if (path.isFile()) { + static const QStringList suffixes = { "hints" }; + return suffixes.contains(path.suffix(), Qt::CaseInsensitive); + } else if (path.isDir()) { + if (!m_root->dir.exists()) + return true; + + static const QStringList fileNames = { "CMakeCache.txt", "build.ninja" }; + + Utils::FilePath dir = path; + while (dir.isChildOf(m_root->dir)) { + for (const QString& fileName : fileNames) { + Utils::FilePath checkFile = dir.pathAppended(fileName); + if (checkFile.exists()) + return true; + } + dir = dir.parentDir(); + } + } + return false; } void CMakeGenerator::createCMakeFiles(const NodePtr &node) const @@ -308,8 +330,7 @@ NodePtr CMakeGenerator::findOrCreateNode(NodePtr &node, const Utils::FilePath &p }; const Utils::FilePath relative = path.relativeChildPath(node->dir); - const QChar separator = relative.pathComponentSeparator(); - const QList components = relative.pathView().split(separator); + const QList components = relative.pathView().split('/'); NodePtr lastNode = node; for (const auto &comp : components) { @@ -445,6 +466,9 @@ void CMakeGenerator::parseNodeTree(NodePtr &generatorNode, { for (const auto *childNode : folderNode->nodes()) { if (const auto *subFolderNode = childNode->asFolderNode()) { + if (ignore(subFolderNode->filePath())) + continue; + NodePtr childGeneratorNode = std::make_shared(); childGeneratorNode->parent = generatorNode; childGeneratorNode->dir = subFolderNode->filePath(); @@ -499,7 +523,10 @@ void CMakeGenerator::compareWithFileSystem(const NodePtr &node) const while (iter.hasNext()) { auto next = Utils::FilePath::fromString(iter.next()); - if (isResource(next) && !findFile(next) && !ignoreFile(next)) + if (ignore(next.parentDir())) + continue; + + if (isResource(next) && !findFile(next) && !ignore(next)) files.push_back(next); } diff --git a/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.h b/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.h index 6077166d5b6..3af3405879f 100644 --- a/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.h +++ b/src/plugins/qmlprojectmanager/cmakegen/cmakegenerator.h @@ -47,7 +47,7 @@ public: private: bool isQml(const Utils::FilePath &path) const; bool isResource(const Utils::FilePath &path) const; - bool ignoreFile(const Utils::FilePath &path) const; + bool ignore(const Utils::FilePath &path) const; void createCMakeFiles(const NodePtr &node) const; void createSourceFiles() const; @@ -70,12 +70,12 @@ private: void compareWithFileSystem(const NodePtr &node) const; bool m_enabled = false; + QmlBuildSystem *m_buildSystem = nullptr; CMakeWriter::Ptr m_writer = {}; QString m_projectName = {}; NodePtr m_root = {}; QStringList m_moduleNames = {}; - QmlBuildSystem *m_buildSystem = nullptr; }; } // namespace GenerateCmake diff --git a/src/plugins/qmlprojectmanager/cmakegen/templates/qmlcomponents.tpl b/src/plugins/qmlprojectmanager/cmakegen/templates/qmlcomponents.tpl index a9f20243a69..5640b85844c 100644 --- a/src/plugins/qmlprojectmanager/cmakegen/templates/qmlcomponents.tpl +++ b/src/plugins/qmlprojectmanager/cmakegen/templates/qmlcomponents.tpl @@ -3,6 +3,7 @@ message("Building designer components.") +set(QT_QDS_COMPONENTS_NOWARN on) set(QT_QML_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/qml") include(FetchContent) @@ -17,6 +18,7 @@ FetchContent_Populate(ds) target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE QuickStudioComponentsplugin + QuickStudioDesignEffectsplugin QuickStudioEffectsplugin QuickStudioApplicationplugin FlowViewplugin diff --git a/src/plugins/qmlprojectmanager/qmlprojectconstants.h b/src/plugins/qmlprojectmanager/qmlprojectconstants.h index a9379799540..ad779481056 100644 --- a/src/plugins/qmlprojectmanager/qmlprojectconstants.h +++ b/src/plugins/qmlprojectmanager/qmlprojectconstants.h @@ -13,7 +13,6 @@ const char customQtForMCUs[] = "CustomQtForMCUs"; const char customQt6Project[] = "CustomQt6Project"; const char mainFilePath[] = "MainFilePath"; -const char customImportPaths[] = "CustomImportPaths"; const char canonicalProjectDir[] ="CanonicalProjectDir"; const char enviromentLaunchedQDS[] = "QTC_LAUNCHED_QDS"; diff --git a/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp b/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp index 5742b945a8d..503738eba52 100644 --- a/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp +++ b/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp @@ -97,12 +97,12 @@ QmlProjectRunConfiguration::QmlProjectRunConfiguration(Target *target, Id id) // arguments from .qmlproject file const QmlBuildSystem *bs = qobject_cast(target->buildSystem()); - for (const QString &importPath : bs->customImportPaths()) { + for (const QString &importPath : bs->absoluteImportPaths()) { cmd.addArg("-I"); - cmd.addArg(bs->targetDirectory().pathAppended(importPath).path()); + cmd.addArg(importPath); } - for (const QString &fileSelector : bs->customFileSelectors()) { + for (const QString &fileSelector : bs->fileSelectors()) { cmd.addArg("-S"); cmd.addArg(fileSelector); } diff --git a/src/plugins/studiowelcome/studiowelcomeplugin.cpp b/src/plugins/studiowelcome/studiowelcomeplugin.cpp index d526a9c4363..6cb94efb35b 100644 --- a/src/plugins/studiowelcome/studiowelcomeplugin.cpp +++ b/src/plugins/studiowelcome/studiowelcomeplugin.cpp @@ -338,30 +338,27 @@ public: if (exampleVersion.isEmpty()) return true; - const QStringList exampleVersionParts = exampleVersion.split('.'); - const QStringList qdsVersionParts = QCoreApplication::applicationVersion().split('.'); + // Split versions into parts (major, minor, patch) + QStringList qdsVersionParts = QCoreApplication::applicationVersion().split('.'); + QStringList exampleVersionParts = exampleVersion.split('.'); - QList exampleVerInts; - QList qdsVerInts; - for (const QString &part : exampleVersionParts) - exampleVerInts.append(part.toInt()); + // Fill missing parts with zeros + while (qdsVersionParts.size() < 3) + qdsVersionParts.append("0"); - for (const QString &part : qdsVersionParts) - qdsVerInts.append(part.toInt()); + while (exampleVersionParts.size() < 3) + exampleVersionParts.append("0"); - // pad zeros so both lists are same size - while (qdsVerInts.size() < exampleVerInts.size()) - qdsVerInts.append(0); + int qdsMajor = qdsVersionParts.at(0).toInt(); + int qdsMinor = qdsVersionParts.at(1).toInt(); + int qdsPatch = qdsVersionParts.at(2).toInt(); - while (exampleVerInts.size() < qdsVerInts.size()) - exampleVerInts.append(0); + int exMajor = exampleVersionParts.at(0).toInt(); + int exMinor = exampleVersionParts.at(1).toInt(); + int exPatch = exampleVersionParts.at(2).toInt(); - for (int i = 0; i < qdsVerInts.size(); ++i) { - if (exampleVerInts[i] < qdsVerInts[i]) - return false; - } - - return true; + return QT_VERSION_CHECK(exMajor, exMinor, exPatch) + <= QT_VERSION_CHECK(qdsMajor, qdsMinor, qdsPatch); } public slots: @@ -587,6 +584,9 @@ static bool forceDownLoad() static bool showSplashScreen() { + // some error dialog is maybe open, be silent to avoid focus problems (macOS had some) + if (Core::ICore::mainWindow() != Core::ICore::dialogParent()) + return false; const Key lastQDSVersionEntry = "QML/Designer/lastQDSVersion"; QtcSettings *settings = Core::ICore::settings(); diff --git a/src/plugins/texteditor/texteditor.cpp b/src/plugins/texteditor/texteditor.cpp index f7f47c10ac0..75117598d35 100644 --- a/src/plugins/texteditor/texteditor.cpp +++ b/src/plugins/texteditor/texteditor.cpp @@ -1314,15 +1314,15 @@ TextEditorWidget::TextEditorWidget(QWidget *parent) { // "Needed", as the creation below triggers ChildEvents that are // passed to this object's event() which uses 'd'. - d = nullptr; - d = new TextEditorWidgetPrivate(this); - + d = std::make_unique(this); setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); setLayoutDirection(Qt::LeftToRight); viewport()->setMouseTracking(true); setFrameStyle(QFrame::NoFrame); } +TextEditorWidget::~TextEditorWidget() = default; + void TextEditorWidget::setTextDocument(const QSharedPointer &doc) { d->setDocument(doc); @@ -1501,12 +1501,6 @@ void TextEditorWidgetPrivate::setDocument(const QSharedPointer &do setupFromDefinition(currentDefinition()); } -TextEditorWidget::~TextEditorWidget() -{ - delete d; - d = nullptr; -} - void TextEditorWidget::print(QPrinter *printer) { const bool oldFullPage = printer->fullPage(); @@ -6391,7 +6385,7 @@ void TextEditorWidgetPrivate::paintRevisionMarker(QPainter &painter, void TextEditorWidget::extraAreaPaintEvent(QPaintEvent *e) { - ExtraAreaPaintEventData data(this, d); + ExtraAreaPaintEventData data(this, d.get()); QTC_ASSERT(data.documentLayout, return); QPainter painter(d->m_extraArea); @@ -10019,7 +10013,7 @@ void TextEditorWidget::setupGenericHighlighter() setLineSeparatorsAllowed(true); connect(textDocument(), &IDocument::filePathChanged, - d, &TextEditorWidgetPrivate::reconfigure); + d.get(), &TextEditorWidgetPrivate::reconfigure); } // diff --git a/src/plugins/texteditor/texteditor.h b/src/plugins/texteditor/texteditor.h index c155bb8ff21..7fc56e87337 100644 --- a/src/plugins/texteditor/texteditor.h +++ b/src/plugins/texteditor/texteditor.h @@ -667,7 +667,7 @@ protected: virtual void slotCodeStyleSettingsChanged(const QVariant &); // Used in CppEditor private: - Internal::TextEditorWidgetPrivate *d; + std::unique_ptr d; friend class BaseTextEditor; friend class TextEditorFactory; friend class Internal::TextEditorFactoryPrivate; diff --git a/src/tools/qml2puppet/CMakeLists.txt b/src/tools/qml2puppet/CMakeLists.txt index b9f1a1544cc..a4e3e44174e 100644 --- a/src/tools/qml2puppet/CMakeLists.txt +++ b/src/tools/qml2puppet/CMakeLists.txt @@ -26,10 +26,7 @@ if (NOT QT_CREATOR_API_DEFINED) COMPONENTS Concurrent Core Gui Network PrintSupport Qml Quick Sql Widgets Xml REQUIRED ) -endif() - -if (NOT TARGET QmlPuppetCommunication) - include(../../libs/qmlpuppetcommunication/QmlPuppetCommunication.cmake) + set(IS_STAND_ALONE_PUPPET_BUILD ON) endif() add_qtc_executable(qml2puppet @@ -44,31 +41,20 @@ add_qtc_executable(qml2puppet SOURCES qml2puppet/qml2puppetmain.cpp qml2puppet/qmlbase.h - qml2puppet/appmetadata.cpp qml2puppet/appmetadata.h - qml2puppet/qmlpuppet.h qml2puppet/qmlpuppet.cpp qml2puppet/configcrashpad.h + qml2puppet/qmlpuppet.h qml2puppet/qmlpuppet.cpp + qml2puppet/configcrashpad.h qmlpuppet.qrc PROPERTIES OUTPUT_NAME qml2puppet-${IDE_VERSION} ) -if (TARGET qml2puppet) - execute_process( - COMMAND git describe --tags --always --dirty=+ - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} - RESULT_VARIABLE GIT_SHA_RESULT - OUTPUT_VARIABLE GIT_SHA - ERROR_VARIABLE GIT_SHA_ERROR - OUTPUT_STRIP_TRAILING_WHITESPACE - ) - - #if we are not a git repository use the .tag file - if(NOT GIT_SHA) - file(STRINGS ${CMAKE_CURRENT_SOURCE_DIR}/../../../.tag GIT_SHA LIMIT_COUNT 1) - endif() - - set(IDE_REVISION_STR ${GIT_SHA}) - - configure_file(../../app/app_version.h.cmakein app/app_version.h ESCAPE_QUOTES) +if (IS_STAND_ALONE_PUPPET_BUILD) + include(../../libs/qmlpuppetcommunication/QmlPuppetCommunication.cmake) + configure_file(../../app/app_version.h.cmakein app/app_version.h ESCAPE_QUOTES) +else() + extend_qtc_executable(qml2puppet + DEPENDS app_version + ) endif() extend_qtc_executable(qml2puppet @@ -107,6 +93,7 @@ extend_qtc_executable(qml2puppet linegeometry.cpp linegeometry.h icongizmoimageprovider.cpp icongizmoimageprovider.h boxgeometry.cpp boxgeometry.h + lookatgeometry.cpp lookatgeometry.h ) find_package(Qt6 COMPONENTS Quick3DAssetImport QUIET) @@ -159,6 +146,7 @@ extend_qtc_executable(qml2puppet qmltransitionnodeinstance.cpp qmltransitionnodeinstance.h qt3dpresentationnodeinstance.cpp qt3dpresentationnodeinstance.h qt5bakelightsnodeinstanceserver.cpp qt5bakelightsnodeinstanceserver.h + qt5import3dnodeinstanceserver.cpp qt5import3dnodeinstanceserver.h qt5informationnodeinstanceserver.cpp qt5informationnodeinstanceserver.h qt5nodeinstanceclientproxy.cpp qt5nodeinstanceclientproxy.h qt5nodeinstanceserver.cpp qt5nodeinstanceserver.h diff --git a/src/tools/qml2puppet/editor3d_qt6.qrc b/src/tools/qml2puppet/editor3d_qt6.qrc index d76b1941b99..85df241f162 100644 --- a/src/tools/qml2puppet/editor3d_qt6.qrc +++ b/src/tools/qml2puppet/editor3d_qt6.qrc @@ -19,6 +19,8 @@ mockfiles/images/spot@2x.png mockfiles/images/preview_landscape.hdr mockfiles/images/preview_studio.hdr + mockfiles/images/reflectionprobe.png + mockfiles/images/reflectionprobe@2x.png mockfiles/qt6/AdjustableArrow.qml mockfiles/qt6/Arrow.qml mockfiles/qt6/AutoScaleHelper.qml @@ -35,6 +37,7 @@ mockfiles/qt6/LightIconGizmo.qml mockfiles/qt6/LightModel.qml mockfiles/qt6/Line3D.qml + mockfiles/qt6/LookAtGizmo.qml mockfiles/qt6/MaterialNodeView.qml mockfiles/qt6/ModelNode2DImageView.qml mockfiles/qt6/ModelNode3DImageView.qml @@ -50,6 +53,7 @@ mockfiles/qt6/PlanarMoveHandle.qml mockfiles/qt6/PlanarScaleHandle.qml mockfiles/qt6/ReflectionProbeBox.qml + mockfiles/qt6/ReflectionProbeGizmo.qml mockfiles/qt6/RotateGizmo.qml mockfiles/qt6/RotateRing.qml mockfiles/qt6/ScaleGizmo.qml diff --git a/src/tools/qml2puppet/mockfiles/images/reflectionprobe.png b/src/tools/qml2puppet/mockfiles/images/reflectionprobe.png new file mode 100644 index 00000000000..4f31a98f659 Binary files /dev/null and b/src/tools/qml2puppet/mockfiles/images/reflectionprobe.png differ diff --git a/src/tools/qml2puppet/mockfiles/images/reflectionprobe@2x.png b/src/tools/qml2puppet/mockfiles/images/reflectionprobe@2x.png new file mode 100644 index 00000000000..2aa3124a414 Binary files /dev/null and b/src/tools/qml2puppet/mockfiles/images/reflectionprobe@2x.png differ diff --git a/src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml b/src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml index 255d93e5295..36d67c534d1 100644 --- a/src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml +++ b/src/tools/qml2puppet/mockfiles/qt6/EditCameraController.qml @@ -26,9 +26,10 @@ 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: _generalHelper.cameraSpeed + readonly property real _keyPanAmount: 1.0 property bool ignoreToolState: false property bool flyMode: viewRoot.flyMode + property bool showCrosshairs: false z: 10 anchors.fill: parent @@ -112,7 +113,7 @@ Item { if (resolvedResult) { var newLookAtAndZoom = _generalHelper.approachNode(camera, _defaultCameraLookAtDistance, - resolvedResult, view3D); + resolvedResult, view3d); _lookAtPoint = newLookAtAndZoom.toVector3d(); _zoomFactor = newLookAtAndZoom.w; storeCameraState(0); @@ -173,11 +174,15 @@ Item { function rotateCamera(angles) { + if (flyMode) + showCrosshairs = true; cameraCtrl._lookAtPoint = _generalHelper.rotateCamera(camera, angles, _lookAtPoint); } function moveCamera(moveVec) { + if (flyMode) + showCrosshairs = true; cameraCtrl._lookAtPoint = _generalHelper.moveCamera(camera, _lookAtPoint, moveVec); } @@ -243,12 +248,19 @@ Item { cameraCtrl._dragging = false; cameraCtrl.storeCameraState(0); } - _generalHelper.stopAllCameraMoves() + showCrosshairs = false; + _generalHelper.stopAllCameraMoves(); + _generalHelper.setCameraSpeedModifier(1.0); + } + + on_LookAtPointChanged: { + viewRoot.overlayViews[splitId].lookAtGizmo.position = _lookAtPoint; } Connections { target: _generalHelper enabled: viewRoot.activeSplit === cameraCtrl.splitId + function onRequestCameraMove(camera, moveVec) { if (camera === cameraCtrl.camera) { cameraCtrl.moveCamera(moveVec); @@ -260,7 +272,7 @@ Item { Image { anchors.centerIn: parent source: "qrc:///qtquickplugin/mockfiles/images/crosshair.png" - visible: cameraCtrl.flyMode && viewRoot.activeSplit === cameraCtrl.splitId + visible: cameraCtrl.showCrosshairs && viewRoot.activeSplit === cameraCtrl.splitId opacity: 0.7 } @@ -317,7 +329,7 @@ Item { onWheel: (wheel) => { if (cameraCtrl.flyMode && cameraCtrl.splitId !== viewRoot.activeSplit) return; - viewRoot.activeSplit = cameraCtrl.splitId + viewRoot.activeSplit = cameraCtrl.splitId; if (cameraCtrl.camera) { // Empirically determined divisor for nice zoom cameraCtrl.zoomRelative(wheel.angleDelta.y / -40); @@ -326,7 +338,17 @@ Item { } } + function setCameraSpeed(event) { + if (event.modifiers === Qt.AltModifier) + _generalHelper.setCameraSpeedModifier(0.5); + else if (event.modifiers === Qt.ShiftModifier) + _generalHelper.setCameraSpeedModifier(2.0); + else + _generalHelper.setCameraSpeedModifier(1.0); + } + Keys.onPressed: (event) => { + setCameraSpeed(event) event.accepted = true; if (cameraCtrl.flyMode && event.key === Qt.Key_Space) approachObject(); @@ -335,6 +357,7 @@ Item { } Keys.onReleased: (event) => { + setCameraSpeed(event) event.accepted = true; _generalHelper.stopCameraMove(cameraCtrl.getMoveVectorForKey(event.key)); } diff --git a/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml b/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml index 23fc1c6a78e..751d21c0e8a 100644 --- a/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml +++ b/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml @@ -25,6 +25,7 @@ Item { property bool showEditLight: false property bool showGrid: true + property bool showLookAt: true property bool showSelectionBox: true property bool showIconGizmo: true property bool showCameraFrustum: false @@ -35,9 +36,10 @@ Item { property color backgroundGradientColorStart: "#222222" property color backgroundGradientColorEnd: "#999999" property color gridColor: "#cccccc" - property bool syncEnvBackground: false + property bool syncEnvBackground: true property bool splitView: false property bool flyMode: false + property bool showCameraSpeed: false enum SelectionMode { Item, Group } enum TransformMode { Move, Rotate, Scale } @@ -65,6 +67,7 @@ Item { onShowEditLightChanged: _generalHelper.storeToolState(sceneId, "showEditLight", showEditLight) onGlobalOrientationChanged: _generalHelper.storeToolState(sceneId, "globalOrientation", globalOrientation) onShowGridChanged: _generalHelper.storeToolState(sceneId, "showGrid", showGrid); + onShowLookAtChanged: _generalHelper.storeToolState(sceneId, "showLookAt", showLookAt); onSyncEnvBackgroundChanged: _generalHelper.storeToolState(sceneId, "syncEnvBackground", syncEnvBackground); onShowSelectionBoxChanged: _generalHelper.storeToolState(sceneId, "showSelectionBox", showSelectionBox); onShowIconGizmoChanged: _generalHelper.storeToolState(sceneId, "showIconGizmo", showIconGizmo); @@ -165,7 +168,7 @@ Item { break; } } - showEditLight = !hasSceneLight; + showEditLight = !hasSceneLight && !_generalHelper.sceneHasLightProbe(sceneId); // Don't inherit camera angles from the previous scene for (let i = 0; i < 4; ++i) @@ -264,16 +267,22 @@ Item { for (var i = 0; i < 4; ++i) { if (syncEnvBackground) { - let bgMode = _generalHelper.sceneEnvironmentBgMode(sceneId); - if ((!_generalHelper.sceneEnvironmentLightProbe(sceneId) && bgMode === SceneEnvironment.SkyBox) - || (!_generalHelper.sceneEnvironmentSkyBoxCubeMap(sceneId) && bgMode === SceneEnvironment.SkyBoxCubeMap)) { - editViews[i].sceneEnv.backgroundMode = SceneEnvironment.Color; - } else { - editViews[i].sceneEnv.backgroundMode = bgMode; + if (_generalHelper.hasSceneEnvironmentData(sceneId)) { + let bgMode = _generalHelper.sceneEnvironmentBgMode(sceneId); + if ((!_generalHelper.sceneEnvironmentLightProbe(sceneId) && bgMode === SceneEnvironment.SkyBox) + || (!_generalHelper.sceneEnvironmentSkyBoxCubeMap(sceneId) && bgMode === SceneEnvironment.SkyBoxCubeMap)) { + editViews[i].sceneEnv.backgroundMode = SceneEnvironment.Color; + } else { + editViews[i].sceneEnv.backgroundMode = bgMode; + } + editViews[i].sceneEnv.lightProbe = _generalHelper.sceneEnvironmentLightProbe(sceneId); + editViews[i].sceneEnv.skyBoxCubeMap = _generalHelper.sceneEnvironmentSkyBoxCubeMap(sceneId); + editViews[i].sceneEnv.clearColor = _generalHelper.sceneEnvironmentColor(sceneId); + } else if (activeScene) { + _generalHelper.updateSceneEnvToLast(editViews[i].sceneEnv, + editViews[i].defaultLightProbe, + editViews[i].defaultCubeMap); } - editViews[i].sceneEnv.lightProbe = _generalHelper.sceneEnvironmentLightProbe(sceneId); - editViews[i].sceneEnv.skyBoxCubeMap = _generalHelper.sceneEnvironmentSkyBoxCubeMap(sceneId); - editViews[i].sceneEnv.clearColor = _generalHelper.sceneEnvironmentColor(sceneId); } else { editViews[i].sceneEnv.backgroundMode = SceneEnvironment.Transparent; editViews[i].sceneEnv.lightProbe = null; @@ -297,11 +306,16 @@ Item { else if (resetToDefault) showGrid = true; + if ("showLookAt" in toolStates) + showLookAt = toolStates.showLookAt; + else if (resetToDefault) + showLookAt = true; + if ("syncEnvBackground" in toolStates) { syncEnvBackground = toolStates.syncEnvBackground; updateEnvBackground(); } else if (resetToDefault) { - syncEnvBackground = false; + syncEnvBackground = true; updateEnvBackground(); } @@ -353,10 +367,13 @@ Item { cameraControls[i].restoreDefaultState(); } - if ("flyMode" in toolStates) + if ("flyMode" in toolStates) { flyMode = toolStates.flyMode; - else if (resetToDefault) + viewRoot.showCameraSpeed = false; + } else if (resetToDefault) { flyMode = false; + viewRoot.showCameraSpeed = false; + } if ("splitView" in toolStates) splitView = toolStates.splitView; @@ -383,6 +400,7 @@ Item { { _generalHelper.storeToolState(sceneId, "showEditLight", showEditLight) _generalHelper.storeToolState(sceneId, "showGrid", showGrid) + _generalHelper.storeToolState(sceneId, "showLookAt", showLookAt) _generalHelper.storeToolState(sceneId, "syncEnvBackground", syncEnvBackground) _generalHelper.storeToolState(sceneId, "showSelectionBox", showSelectionBox) _generalHelper.storeToolState(sceneId, "showIconGizmo", showIconGizmo) @@ -519,6 +537,12 @@ Item { overlayViews[i].addParticleEmitterGizmo(scene, obj); } + function addReflectionProbeGizmo(scene, obj) + { + for (var i = 0; i < 4; ++i) + overlayViews[i].addReflectionProbeGizmo(scene, obj); + } + function releaseLightGizmo(obj) { for (var i = 0; i < 4; ++i) @@ -543,6 +567,12 @@ Item { overlayViews[i].releaseParticleEmitterGizmo(obj); } + function releaseReflectionProbeGizmo(obj) + { + for (var i = 0; i < 4; ++i) + overlayViews[i].releaseReflectionProbeGizmo(obj); + } + function updateLightGizmoScene(scene, obj) { for (var i = 0; i < 4; ++i) @@ -567,6 +597,12 @@ Item { overlayViews[i].updateParticleEmitterGizmoScene(scene, obj); } + function updateReflectionProbeGizmoScene(scene, obj) + { + for (var i = 0; i < 4; ++i) + overlayViews[i].updateReflectionProbeGizmoScene(scene, obj); + } + function resolveSplitPoint(x, y) { if (!splitView || activeSplit === 0) @@ -638,7 +674,6 @@ Item { { for (var i = 0; i < 4; ++i) overlayViews[i].handleHiddenStateChange(node); - } function onUpdateDragTooltip() @@ -651,6 +686,18 @@ Item { { updateEnvBackground(); } + + function onCameraSpeedChanged() { + _generalHelper.requestTimerEvent("hideSpeed", 1000); + viewRoot.showCameraSpeed = true + } + + function onRequestedTimerEvent(timerId) { + if (timerId === "hideSpeed") { + viewRoot.showCameraSpeed = false; + _generalHelper.requestRender(); + } + } } // Shared nodes of the overlay, set as importScene on all overlay views. @@ -1058,6 +1105,38 @@ Item { color: "white" visible: viewRoot.fps > 0 } + + Rectangle { + id: cameraSpeedLabel + width: 120 + height: 65 + anchors.centerIn: parent + opacity: 0.6 + radius: 10 + color: "white" + visible: flyMode && viewRoot.showCameraSpeed + enabled: false + + Column { + anchors.fill: parent + anchors.margins: 8 + spacing: 2 + Text { + width: parent.width + horizontalAlignment: Text.AlignHCenter + text: "Camera Speed" + font.pixelSize: 16 + } + Text { + width: parent.width + height: 20 + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + font.pixelSize: 20 + text: _generalHelper.cameraSpeed.toLocaleString(Qt.locale(), 'f', 1) + } + } + } } Keys.onPressed: (event) => { diff --git a/src/tools/qml2puppet/mockfiles/qt6/IconGizmo.qml b/src/tools/qml2puppet/mockfiles/qt6/IconGizmo.qml index acde896aeba..50c4aab1e73 100644 --- a/src/tools/qml2puppet/mockfiles/qt6/IconGizmo.qml +++ b/src/tools/qml2puppet/mockfiles/qt6/IconGizmo.qml @@ -29,7 +29,7 @@ Item { property alias iconSource: iconImage.source - signal clicked(Node node, bool multi) + signal clicked(Node node, int button, bool multi) function hasPoint(x, y) { @@ -83,7 +83,7 @@ Item { } onClicked: (mouse)=> { - iconGizmo.clicked(iconGizmo.targetNode, + iconGizmo.clicked(iconGizmo.targetNode, mouse.button, mouse.modifiers & Qt.ControlModifier); } hoverEnabled: iconGizmo.highlightOnHover && !iconGizmo.selected diff --git a/src/tools/qml2puppet/mockfiles/qt6/LookAtGizmo.qml b/src/tools/qml2puppet/mockfiles/qt6/LookAtGizmo.qml new file mode 100644 index 00000000000..71527d9fb3d --- /dev/null +++ b/src/tools/qml2puppet/mockfiles/qt6/LookAtGizmo.qml @@ -0,0 +1,27 @@ +// 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 QtQuick3D +import LookAtGeometry + +Node { + id: root + + property alias crossScale: lookAtGeometry.crossScale + property alias color: lookAtMat.baseColor + + Model { + readonly property bool _edit3dLocked: true // Make this non-pickable + geometry: LookAtGeometry { + id: lookAtGeometry + } + materials: [ + PrincipledMaterial { + id: lookAtMat + lighting: DefaultMaterial.NoLighting + cullMode: Material.NoCulling + } + ] + } +} diff --git a/src/tools/qml2puppet/mockfiles/qt6/OverlayView3D.qml b/src/tools/qml2puppet/mockfiles/qt6/OverlayView3D.qml index 13a37f4f723..464e0b6b79c 100644 --- a/src/tools/qml2puppet/mockfiles/qt6/OverlayView3D.qml +++ b/src/tools/qml2puppet/mockfiles/qt6/OverlayView3D.qml @@ -12,6 +12,7 @@ View3D { property alias rotateGizmo: rotateGizmo property alias scaleGizmo: scaleGizmo property alias lightGizmo: lightGizmo + property alias lookAtGizmo: lookAtGizmo property var viewRoot: null property View3D editView: null @@ -21,6 +22,7 @@ View3D { property var cameraGizmos: [] property var particleSystemIconGizmos: [] property var particleEmitterGizmos: [] + property var reflectionProbeGizmos: [] signal commitObjectProperty(var objects, var propNames) signal changeObjectProperty(var objects, var propNames) @@ -288,66 +290,141 @@ View3D { } } + function addReflectionProbeGizmo(scene, obj) + { + // Insert into first available gizmo if we don't already have gizmo for this object + var slotFound = -1; + for (var i = 0; i < reflectionProbeGizmos.length; ++i) { + if (!reflectionProbeGizmos[i].targetNode) { + slotFound = i; + } else if (reflectionProbeGizmos[i].targetNode === obj) { + reflectionProbeGizmos[i].scene = scene; + return; + } + } + + if (slotFound !== -1) { + reflectionProbeGizmos[slotFound].scene = scene; + reflectionProbeGizmos[slotFound].targetNode = obj; + reflectionProbeGizmos[slotFound].locked = _generalHelper.isLocked(obj); + reflectionProbeGizmos[slotFound].hidden = _generalHelper.isHidden(obj); + return; + } + + // No free gizmos available, create a new one + var gizmoComponent = Qt.createComponent("ReflectionProbeGizmo.qml"); + if (gizmoComponent.status === Component.Ready) { + var gizmo = gizmoComponent.createObject(overlayView, + {"view3D": overlayView, + "targetNode": obj, + "selectedNodes": viewRoot.selectedNodes, + "scene": scene, + "activeScene": viewRoot.activeScene, + "locked": _generalHelper.isLocked(obj), + "hidden": _generalHelper.isHidden(obj), + "globalShow": viewRoot.showIconGizmo}); + reflectionProbeGizmos[reflectionProbeGizmos.length] = gizmo; + gizmo.clicked.connect(viewRoot.handleObjectClicked); + gizmo.selectedNodes = Qt.binding(function() {return viewRoot.selectedNodes;}); + gizmo.activeScene = Qt.binding(function() {return viewRoot.activeScene;}); + gizmo.globalShow = Qt.binding(function() {return viewRoot.showIconGizmo;}); + } + } + + function releaseReflectionProbeGizmo(obj) + { + for (var i = 0; i < reflectionProbeGizmos.length; ++i) { + if (reflectionProbeGizmos[i].targetNode === obj) { + reflectionProbeGizmos[i].scene = null; + reflectionProbeGizmos[i].targetNode = null; + return; + } + } + } + + function updateReflectionProbeGizmoScene(scene, obj) + { + for (var i = 0; i < reflectionProbeGizmos.length; ++i) { + if (reflectionProbeGizmos[i].targetNode === obj) { + reflectionProbeGizmos[i].scene = scene; + return; + } + } + } + function gizmoAt(x, y) { - for (var i = 0; i < lightIconGizmos.length; ++i) { + let i = 0; + for (; i < lightIconGizmos.length; ++i) { if (lightIconGizmos[i].visible && lightIconGizmos[i].hasPoint(x, y)) return lightIconGizmos[i].targetNode; } - for (var i = 0; i < cameraGizmos.length; ++i) { + for (i = 0; i < cameraGizmos.length; ++i) { if (cameraGizmos[i].visible && cameraGizmos[i].hasPoint(x, y)) return cameraGizmos[i].targetNode; } - for (var i = 0; i < particleSystemIconGizmos.length; ++i) { + for (i = 0; i < particleSystemIconGizmos.length; ++i) { if (particleSystemIconGizmos[i].visible && particleSystemIconGizmos[i].hasPoint(x, y)) return particleSystemIconGizmos[i].targetNode; } + for (i = 0; i < reflectionProbeGizmos.length; ++i) { + if (reflectionProbeGizmos[i].visible && reflectionProbeGizmos[i].hasPoint(x, y)) + return reflectionProbeGizmos[i].targetNode; + } return null; } function handleLockedStateChange(node) { - for (var i = 0; i < lightIconGizmos.length; ++i) { + let i = 0; + for (; i < lightIconGizmos.length; ++i) { if (lightIconGizmos[i].targetNode === node) { lightIconGizmos[i].locked = _generalHelper.isLocked(node); return; } } - for (var i = 0; i < cameraGizmos.length; ++i) { + for (i = 0; i < cameraGizmos.length; ++i) { if (cameraGizmos[i].targetNode === node) { cameraGizmos[i].locked = _generalHelper.isLocked(node); return; } } - for (var i = 0; i < particleSystemIconGizmos.length; ++i) { + for (i = 0; i < particleSystemIconGizmos.length; ++i) { if (particleSystemIconGizmos[i].targetNode === node) { particleSystemIconGizmos[i].locked = _generalHelper.isLocked(node); return; } } + for (i = 0; i < reflectionProbeGizmos.length; ++i) { + if (reflectionProbeGizmos[i].targetNode === node) { + reflectionProbeGizmos[i].locked = _generalHelper.isLocked(node); + return; + } + } } function handleHiddenStateChange(node) { - for (var i = 0; i < lightIconGizmos.length; ++i) { + let i = 0; + for (; i < lightIconGizmos.length; ++i) { if (lightIconGizmos[i].targetNode === node) { lightIconGizmos[i].hidden = _generalHelper.isHidden(node); return; } } - for (var i = 0; i < cameraGizmos.length; ++i) { + for (i = 0; i < cameraGizmos.length; ++i) { if (cameraGizmos[i].targetNode === node) { cameraGizmos[i].hidden = _generalHelper.isHidden(node); return; } } - for (var i = 0; i < particleSystemIconGizmos.length; ++i) { + for (i = 0; i < particleSystemIconGizmos.length; ++i) { if (particleSystemIconGizmos[i].targetNode === node) { particleSystemIconGizmos[i].hidden = _generalHelper.isHidden(node); return; } } - for (var i = 0; i < particleEmitterGizmos.length; ++i) { + for (i = 0; i < particleEmitterGizmos.length; ++i) { if (particleEmitterGizmos[i].targetNode === node) { particleEmitterGizmos[i].hidden = _generalHelper.isHidden(node); return; @@ -356,6 +433,12 @@ View3D { return; } } + for (i = 0; i < reflectionProbeGizmos.length; ++i) { + if (reflectionProbeGizmos[i].targetNode === node) { + reflectionProbeGizmos[i].hidden = _generalHelper.isHidden(node); + return; + } + } } SceneEnvironment { @@ -402,6 +485,12 @@ View3D { position: pivotLine.startPos } + AutoScaleHelper { + id: lookAtAutoScale + view3D: overlayView + position: lookAtGizmo.scenePosition + } + MoveGizmo { id: moveGizmo scale: autoScale.getScale(Qt.vector3d(5, 5, 5)) @@ -537,5 +626,14 @@ View3D { ] } } + + LookAtGizmo { + id: lookAtGizmo + color: "#ddd600" + scale: lookAtAutoScale.getScale(Qt.vector3d(10, 10, 10)) + visible: overlayView.viewRoot.showLookAt + && overlayView.isActive + && !overlayView.viewRoot.cameraControls[viewRoot.activeSplit].showCrosshairs + } } } diff --git a/src/tools/qml2puppet/mockfiles/qt6/ReflectionProbeGizmo.qml b/src/tools/qml2puppet/mockfiles/qt6/ReflectionProbeGizmo.qml new file mode 100644 index 00000000000..2183e68679a --- /dev/null +++ b/src/tools/qml2puppet/mockfiles/qt6/ReflectionProbeGizmo.qml @@ -0,0 +1,9 @@ +// 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 QtQuick3D + +IconGizmo { + iconSource: "qrc:///qtquickplugin/mockfiles/images/reflectionprobe.png" +} diff --git a/src/tools/qml2puppet/mockfiles/qt6/SceneView3D.qml b/src/tools/qml2puppet/mockfiles/qt6/SceneView3D.qml index 1de1cebd14a..82c735467ec 100644 --- a/src/tools/qml2puppet/mockfiles/qt6/SceneView3D.qml +++ b/src/tools/qml2puppet/mockfiles/qt6/SceneView3D.qml @@ -15,6 +15,8 @@ View3D { property alias perspectiveCamera: scenePerspectiveCamera property alias orthoCamera: sceneOrthoCamera property alias sceneEnv: sceneEnv + property alias defaultLightProbe: defaultLightProbe + property alias defaultCubeMap: defaultCubeMap property vector3d cameraLookAt property var selectionBoxes: [] property Node selectedNode @@ -61,6 +63,14 @@ View3D { id: sceneEnv antialiasingMode: SceneEnvironment.MSAA antialiasingQuality: SceneEnvironment.High + + Texture { + id: defaultLightProbe + } + + CubeMapTexture { + id: defaultCubeMap + } } Node { diff --git a/src/tools/qml2puppet/qml2puppet/appmetadata.cpp b/src/tools/qml2puppet/qml2puppet/appmetadata.cpp deleted file mode 100644 index 1896e4db926..00000000000 --- a/src/tools/qml2puppet/qml2puppet/appmetadata.cpp +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (C) 2022 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 "appmetadata.h" - -#include - -namespace QDSMeta::AppInfo { - -void printAppInfo() -{ - qInfo() << Qt::endl - << "<< QDS Meta Info >>" << Qt::endl - << "App Info" << Qt::endl - << " - Name :" << Core::Constants::IDE_ID << Qt::endl - << " - Version :" << Core::Constants::IDE_VERSION_DISPLAY << Qt::endl - << " - Author :" << Core::Constants::IDE_AUTHOR << Qt::endl - << " - Year :" << Core::Constants::IDE_YEAR << Qt::endl - << " - App :" << QCoreApplication::applicationName() << Qt::endl - << "Build Info " << Qt::endl - << " - Date :" << __DATE__ << Qt::endl - << " - Commit :" << QStringLiteral(QDS_STRINGIFY(IDE_REVISION_STR)) << Qt::endl - << " - Qt Version :" << QT_VERSION_STR << Qt::endl - << "Compiler Info " << Qt::endl -#if defined(__GNUC__) - << " - GCC :" << __GNUC__ << Qt::endl - << " - GCC Minor :" << __GNUC_MINOR__ << Qt::endl - << " - GCC Patch :" << __GNUC_PATCHLEVEL__ << Qt::endl -#endif -#if defined(_MSC_VER) - << " - MSC Short :" << _MSC_VER << Qt::endl - << " - MSC Full :" << _MSC_FULL_VER << Qt::endl -#endif -#if defined(__clang__) - << " - clang maj :" << __clang_major__ << Qt::endl - << " - clang min :" << __clang_minor__ << Qt::endl - << " - clang patch :" << __clang_patchlevel__ << Qt::endl -#endif - << "<< End Of QDS Meta Info >>" << Qt::endl; - exit(0); -} - -void registerAppInfo(const QString &appName) -{ - QCoreApplication::setOrganizationName(Core::Constants::IDE_AUTHOR); - QCoreApplication::setOrganizationDomain("qt-project.org"); - QCoreApplication::setApplicationName(appName); - QCoreApplication::setApplicationVersion(Core::Constants::IDE_VERSION_LONG); -} - -} // namespace QDSMeta::AppInfo diff --git a/src/tools/qml2puppet/qml2puppet/appmetadata.h b/src/tools/qml2puppet/qml2puppet/appmetadata.h deleted file mode 100644 index 18eb650461e..00000000000 --- a/src/tools/qml2puppet/qml2puppet/appmetadata.h +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -#pragma once - -#include -#include - -// Common functions can be used in all QDS apps -namespace QDSMeta { - -namespace Logging { -inline Q_LOGGING_CATEGORY(deprecated, "qt.tools.qds.deprecated"); -inline Q_LOGGING_CATEGORY(verbose1, "qt.tools.qds.verbose1"); -inline Q_LOGGING_CATEGORY(verbose2, "qt.tools.qds.verbose2"); - -inline void registerMessageHandler() -{ - qInstallMessageHandler( - [](QtMsgType type, const QMessageLogContext &context, const QString &msg) { - auto tPrinter = [&](const QString &msgPrefix) { - fprintf(stderr, - "%s: %s (%s:%u, %s)\n", - msgPrefix.toLocal8Bit().constData(), - msg.toLocal8Bit().constData(), - context.file, - context.line, - context.function); - }; - - if (type == QtDebugMsg) - tPrinter("Debug"); - else if (type == QtInfoMsg) - tPrinter("Info"); - else if (type == QtWarningMsg) - tPrinter("Warning"); - else if (type == QtCriticalMsg) - tPrinter("Critical"); - else if (type == QtFatalMsg) { - tPrinter("Fatal"); - abort(); - } - }); -} -} // namespace Logging - -namespace AppInfo { - -#define STRINGIFY_INTERNAL(x) #x -#define QDS_STRINGIFY(x) STRINGIFY_INTERNAL(x) - -void printAppInfo(); -void registerAppInfo(const QString &appName); - -} // namespace AppInfo -} // namespace QDSMeta diff --git a/src/tools/qml2puppet/qml2puppet/configcrashpad.h b/src/tools/qml2puppet/qml2puppet/configcrashpad.h index 70cbfc5f4a5..b802b809575 100644 --- a/src/tools/qml2puppet/qml2puppet/configcrashpad.h +++ b/src/tools/qml2puppet/qml2puppet/configcrashpad.h @@ -5,6 +5,7 @@ #include #if defined(ENABLE_CRASHPAD) && defined(Q_OS_WIN) +#include #define NOMINMAX #include "client/crash_report_database.h" #include "client/crashpad_client.h" diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp index dadd8176330..3baf91c1464 100644 --- a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp +++ b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp @@ -6,6 +6,8 @@ #include "selectionboxgeometry.h" +#include + #include #include #include @@ -42,9 +44,17 @@ namespace QmlDesigner { namespace Internal { -const QString _globalStateId = QStringLiteral("@GTS"); // global tool state +const QString _globalStateId = QStringLiteral("@GTS"); // global tool state (within document) +const QString _projectStateId = QStringLiteral("@PTS"); // project wide tool state const QString _lastSceneIdKey = QStringLiteral("lastSceneId"); const QString _rootSizeKey = QStringLiteral("rootSize"); +const QString _lastSceneEnvKey = QStringLiteral("lastSceneEnv"); + +const QString _lightProbeProp = QStringLiteral("lightProbe"); +const QString _sourceProp = QStringLiteral("source"); +const QString _cubeProp = QStringLiteral("skyBoxCubeMap"); +const QString _bgProp = QStringLiteral("backgroundMode"); +const QString _colorProp = QStringLiteral("clearColor"); static const float floatMin = std::numeric_limits::lowest(); static const float floatMax = std::numeric_limits::max(); @@ -163,16 +173,17 @@ QVector3D GeneralHelper::moveCamera(QQuick3DCamera *camera, const QVector3D &sta if (moveVector.length() < 0.001f) return startLookAt; + QVector3D speedVector = moveVector * m_cameraSpeed * m_cameraSpeedModifier; + 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(); - // Delta multiplier for nice default speed in default scene - const QVector3D delta = (yDelta - xDelta - zDelta) * .5f; + const QVector3D xDelta = xAxis * speedVector.x(); + const QVector3D yDelta = yAxis * speedVector.y(); + const QVector3D zDelta = zAxis * speedVector.z(); + const QVector3D delta = (yDelta - xDelta - zDelta); camera->setPosition(camera->position() + delta); @@ -451,17 +462,19 @@ QVector4D GeneralHelper::approachNode( // a selection box for bound calculations to work. This is used to focus the view for // various preview image generations, where doing things asynchronously is not good // and recalculating bounds for every frame is not a problem. -void GeneralHelper::calculateNodeBoundsAndFocusCamera( - QQuick3DCamera *camera, QQuick3DNode *node, QQuick3DViewport *viewPort, - float defaultLookAtDistance, bool closeUp) +void GeneralHelper::calculateBoundsAndFocusCamera(QQuick3DCamera *camera, QQuick3DNode *node, + QQuick3DViewport *viewPort, + float defaultLookAtDistance, + bool closeUp, QVector3D &lookAt, + QVector3D &extents) { QVector3D minBounds; QVector3D maxBounds; getBounds(viewPort, node, minBounds, maxBounds); - QVector3D extents = maxBounds - minBounds; - QVector3D lookAt = minBounds + (extents / 2.f); + extents = maxBounds - minBounds; + lookAt = minBounds + (extents / 2.f); float maxExtent = qSqrt(qreal(extents.x()) * qreal(extents.x()) + qreal(extents.y()) * qreal(extents.y()) + qreal(extents.z()) * qreal(extents.z())); @@ -494,10 +507,20 @@ void GeneralHelper::calculateNodeBoundsAndFocusCamera( perspectiveCamera->setClipNear(minDist * 0.99); perspectiveCamera->setClipFar(maxDist * 1.01); } - } } +void GeneralHelper::calculateNodeBoundsAndFocusCamera(QQuick3DCamera *camera, QQuick3DNode *node, + QQuick3DViewport *viewPort, + float defaultLookAtDistance, + bool closeUp) +{ + QVector3D extents; + QVector3D lookAt; + calculateBoundsAndFocusCamera(camera, node, viewPort, defaultLookAtDistance, closeUp, + lookAt, extents); +} + // Aligns any cameras found in nodes list to a camera. // Only position and rotation are copied, rest of the camera properties stay the same. void GeneralHelper::alignCameras(QQuick3DCamera *camera, const QVariant &nodes) @@ -738,6 +761,11 @@ void GeneralHelper::setSceneEnvironmentData(const QString &sceneId, } } +bool GeneralHelper::hasSceneEnvironmentData(const QString &sceneId) const +{ + return m_sceneEnvironmentData.contains(sceneId); +} + QQuick3DSceneEnvironment::QQuick3DEnvironmentBackgroundTypes GeneralHelper::sceneEnvironmentBgMode( const QString &sceneId) const { @@ -759,6 +787,69 @@ QQuick3DCubeMapTexture *GeneralHelper::sceneEnvironmentSkyBoxCubeMap(const QStri return m_sceneEnvironmentData[sceneId].skyBoxCubeMap.data(); } +void GeneralHelper::updateSceneEnvToLast(QQuick3DSceneEnvironment *env, QQuick3DTexture *lightProbe, + QQuick3DCubeMapTexture *cubeMap) +{ + if (!env) + return; + + if (m_lastSceneEnvData.contains(_bgProp)) { + Enumeration enumeration = m_lastSceneEnvData[_bgProp].value(); + QMetaEnum me = QMetaEnum::fromType(); + int intValue = me.keyToValue(enumeration.toName()); + env->setBackgroundMode(QQuick3DSceneEnvironment::QQuick3DEnvironmentBackgroundTypes(intValue)); + } else { + env->setBackgroundMode(QQuick3DSceneEnvironment::Transparent); + } + + if (m_lastSceneEnvData.contains(_colorProp)) + env->setClearColor(m_lastSceneEnvData[_colorProp].value()); + else + env->setClearColor(Qt::transparent); + + if (lightProbe) { + if (m_lastSceneEnvData.contains(_lightProbeProp)) { + QVariantMap props = m_lastSceneEnvData[_lightProbeProp].toMap(); + if (props.contains(_sourceProp)) + lightProbe->setSource(props[_sourceProp].toUrl()); + else + lightProbe->setSource({}); + env->setLightProbe(lightProbe); + } else { + env->setLightProbe(nullptr); + } + } + + if (cubeMap) { + if (m_lastSceneEnvData.contains(_cubeProp)) { + QVariantMap props = m_lastSceneEnvData[_cubeProp].toMap(); + if (props.contains(_sourceProp)) + cubeMap->setSource(props[_sourceProp].toUrl()); + else + cubeMap->setSource({}); + env->setSkyBoxCubeMap(cubeMap); + } else { + env->setSkyBoxCubeMap(nullptr); + } + } +} + +bool GeneralHelper::sceneHasLightProbe(const QString &sceneId) +{ + // From editor perspective, a scene is considered to have a light probe if scene itself + // has a light probe or scene has no env data and last scene had a light probe + if (m_sceneEnvironmentData.contains(sceneId)) { + return bool(m_sceneEnvironmentData[sceneId].lightProbe); + } else { + if (m_lastSceneEnvData.contains(_lightProbeProp)) { + QVariantMap props = m_lastSceneEnvData[_sourceProp].toMap(); + if (props.contains(_sourceProp)) + return !props[_sourceProp].toUrl().isEmpty(); + } + } + return false; +} + void GeneralHelper::clearSceneEnvironmentData() { for (const SceneEnvData &data : std::as_const(m_sceneEnvironmentData)) { @@ -772,6 +863,12 @@ void GeneralHelper::clearSceneEnvironmentData() emit sceneEnvDataChanged(); } +void GeneralHelper::setLastSceneEnvironmentData(const QVariantMap &data) +{ + m_lastSceneEnvData = data; + storeToolState(_projectStateId, _lastSceneEnvKey, m_lastSceneEnvData); +} + void GeneralHelper::initToolStates(const QString &sceneId, const QVariantMap &toolStates) { m_toolStates[sceneId] = toolStates; @@ -796,11 +893,21 @@ QString GeneralHelper::globalStateId() const return _globalStateId; } +QString GeneralHelper::projectStateId() const +{ + return _projectStateId; +} + QString GeneralHelper::lastSceneIdKey() const { return _lastSceneIdKey; } +QString GeneralHelper::lastSceneEnvKey() const +{ + return _lastSceneEnvKey; +} + QString GeneralHelper::rootSizeKey() const { return _rootSizeKey; @@ -1187,6 +1294,11 @@ void GeneralHelper::setCameraSpeed(double speed) } } +void GeneralHelper::setCameraSpeedModifier(double modifier) +{ + m_cameraSpeedModifier = modifier; +} + QString GeneralHelper::formatVectorDragTooltip(const QVector3D &vec, const QString &suffix) const { return QObject::tr("x:%L1 y:%L2 z:%L3%L4") @@ -1414,6 +1526,26 @@ bool GeneralHelper::compareQuaternions(const QQuaternion &q1, const QQuaternion && qFuzzyCompare(q1.z(), q2.z()) && qFuzzyCompare(q1.scalar(), q2.scalar()); } +void GeneralHelper::requestTimerEvent(const QString &timerId, qint64 delay) +{ + if (m_eventTimers.contains(timerId)) { + m_eventTimers[timerId]->start(delay); + } else { + auto timer = new QTimer; + timer->setInterval(delay); + timer->setSingleShot(true); + connect(timer, &QTimer::timeout, this, [this, timerId]() { + if (m_eventTimers.contains(timerId)) { + QTimer *timer = m_eventTimers.take(timerId); + timer->deleteLater(); + } + emit requestedTimerEvent(timerId); + }); + m_eventTimers[timerId] = timer; + timer->start(delay); + } +} + } } diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h index 62a95d86a6f..f1b57f4122f 100644 --- a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h +++ b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h @@ -72,6 +72,9 @@ public: bool closeUp = false); Q_INVOKABLE QVector4D approachNode(QQuick3DCamera *camera, float defaultLookAtDistance, QObject *node, QQuick3DViewport *viewPort); + void calculateBoundsAndFocusCamera(QQuick3DCamera *camera, QQuick3DNode *node, + QQuick3DViewport *viewPort, float defaultLookAtDistance, + bool closeUp, QVector3D &lookAt, QVector3D &extents); Q_INVOKABLE void calculateNodeBoundsAndFocusCamera(QQuick3DCamera *camera, QQuick3DNode *node, QQuick3DViewport *viewPort, float defaultLookAtDistance, bool closeUp); @@ -96,7 +99,9 @@ public: Q_INVOKABLE void enableItemUpdate(QQuickItem *item, bool enable); Q_INVOKABLE QVariantMap getToolStates(const QString &sceneId); QString globalStateId() const; + QString projectStateId() const; QString lastSceneIdKey() const; + QString lastSceneEnvKey() const; QString rootSizeKey() const; Q_INVOKABLE void setMultiSelectionTargets(QQuick3DNode *multiSelectRootNode, @@ -109,12 +114,18 @@ public: Q_INVOKABLE void rotateMultiSelection(bool commit); void setSceneEnvironmentData(const QString &sceneId, QQuick3DSceneEnvironment *env); + Q_INVOKABLE bool hasSceneEnvironmentData(const QString &sceneId) const; Q_INVOKABLE QQuick3DSceneEnvironment::QQuick3DEnvironmentBackgroundTypes sceneEnvironmentBgMode( const QString &sceneId) const; Q_INVOKABLE QColor sceneEnvironmentColor(const QString &sceneId) const; Q_INVOKABLE QQuick3DTexture *sceneEnvironmentLightProbe(const QString &sceneId) const; Q_INVOKABLE QQuick3DCubeMapTexture *sceneEnvironmentSkyBoxCubeMap(const QString &sceneId) const; + Q_INVOKABLE void updateSceneEnvToLast(QQuick3DSceneEnvironment *env, QQuick3DTexture *lightProbe, + QQuick3DCubeMapTexture *cubeMap); + Q_INVOKABLE bool sceneHasLightProbe(const QString &sceneId); + void clearSceneEnvironmentData(); + void setLastSceneEnvironmentData(const QVariantMap &data); bool isMacOS() const; @@ -139,6 +150,7 @@ public: void setSnapRotationInterval(double interval) { m_snapRotationInterval = interval; } void setSnapScaleInterval(double interval) { m_snapScaleInterval = interval / 100.; } void setCameraSpeed(double speed); + Q_INVOKABLE void setCameraSpeedModifier(double modifier); Q_INVOKABLE QString snapPositionDragTooltip(const QVector3D &pos) const; Q_INVOKABLE QString snapRotationDragTooltip(double angle) const; @@ -154,6 +166,8 @@ public: Q_INVOKABLE bool compareVectors(const QVector3D &v1, const QVector3D &v2) const; Q_INVOKABLE bool compareQuaternions(const QQuaternion &q1, const QQuaternion &q2) const; + Q_INVOKABLE void requestTimerEvent(const QString &timerId, qint64 delay); + signals: void overlayUpdateNeeded(); void toolStateChanged(const QString &sceneId, const QString &tool, const QVariant &toolState); @@ -167,6 +181,7 @@ signals: void requestCameraMove(QQuick3DCamera *camera, const QVector3D &moveVector); void requestRender(); void cameraSpeedChanged(); + void requestedTimerEvent(const QString &timerId); private: void handlePendingToolStateUpdate(); @@ -198,6 +213,7 @@ private: QPointer skyBoxCubeMap; }; QHash m_sceneEnvironmentData; + QVariantMap m_lastSceneEnvData; struct MultiSelData { QVector3D startScenePos; @@ -221,8 +237,10 @@ private: double m_snapRotationInterval = 5.; double m_snapScaleInterval = .1; double m_cameraSpeed = 10.; + double m_cameraSpeedModifier = 1.; QVariant m_bgColor; + QHash m_eventTimers; }; } diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/lookatgeometry.cpp b/src/tools/qml2puppet/qml2puppet/editor3d/lookatgeometry.cpp new file mode 100644 index 00000000000..b569f6ea5ab --- /dev/null +++ b/src/tools/qml2puppet/qml2puppet/editor3d/lookatgeometry.cpp @@ -0,0 +1,62 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#ifdef QUICK3D_MODULE + +#include "lookatgeometry.h" + +namespace QmlDesigner::Internal { + +LookAtGeometry::LookAtGeometry() + : GeometryBase() + , m_crossScale(1.f, 1.f, 1.f) +{ +} + +LookAtGeometry::~LookAtGeometry() +{ +} + +QVector3D LookAtGeometry::crossScale() const +{ + return m_crossScale; +} + +void LookAtGeometry::setCrossScale(const QVector3D &scale) +{ + if (scale != m_crossScale) { + m_crossScale = scale; + emit crossScaleChanged(); + updateGeometry(); + } +} + +void LookAtGeometry::doUpdateGeometry() +{ + GeometryBase::doUpdateGeometry(); + + QByteArray vertexData; + vertexData.resize(6 * 3 * 4); // 6 vertices of 3 floats each 4 bytes + float *dataPtr = reinterpret_cast(vertexData.data()); + + auto addVertex = [&dataPtr](float x, float y, float z) { + dataPtr[0] = x; + dataPtr[1] = y; + dataPtr[2] = z; + dataPtr += 3; + }; + + addVertex(m_crossScale.x(), 0.f, 0.f); + addVertex(-m_crossScale.x(), 0.f, 0.f); + addVertex(0.f, m_crossScale.y(), 0.f); + addVertex(0.f, -m_crossScale.y(), 0.f); + addVertex(0.f, 0.f, m_crossScale.z()); + addVertex(0.f, 0.f, -m_crossScale.z()); + + setVertexData(vertexData); + setBounds(-m_crossScale, m_crossScale); +} + +} // namespace QmlDesigner::Internal + +#endif // QUICK3D_MODULE diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/lookatgeometry.h b/src/tools/qml2puppet/qml2puppet/editor3d/lookatgeometry.h new file mode 100644 index 00000000000..e881c2750e1 --- /dev/null +++ b/src/tools/qml2puppet/qml2puppet/editor3d/lookatgeometry.h @@ -0,0 +1,42 @@ +// 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 + +#ifdef QUICK3D_MODULE + +#include "geometrybase.h" + +#include + +namespace QmlDesigner::Internal { + +class LookAtGeometry : public GeometryBase +{ + Q_OBJECT + Q_PROPERTY(QVector3D crossScale READ crossScale WRITE setCrossScale NOTIFY crossScaleChanged) + +public: + LookAtGeometry(); + ~LookAtGeometry() override; + + QVector3D crossScale() const; + +public slots: + void setCrossScale(const QVector3D &pos); + +signals: + void crossScaleChanged(); + +protected: + void doUpdateGeometry() override; + +private: + QVector3D m_crossScale; +}; + +} // namespace QmlDesigner::Internal + +QML_DECLARE_TYPE(QmlDesigner::Internal::LookAtGeometry) + +#endif // QUICK3D_MODULE diff --git a/src/tools/qml2puppet/qml2puppet/instances/nodeinstanceserver.cpp b/src/tools/qml2puppet/qml2puppet/instances/nodeinstanceserver.cpp index f3227e386cc..0f7133b1ff5 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/nodeinstanceserver.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/nodeinstanceserver.cpp @@ -1187,8 +1187,9 @@ InformationChangedCommand NodeInstanceServer::createAllInformationChangedCommand static bool supportedVariantType(int type) { - return type < int(QVariant::UserType) && type != QMetaType::QObjectStar - && type != QMetaType::QModelIndex && type != QMetaType::VoidStar; + return (type < int(QVariant::UserType) && type != QMetaType::QObjectStar + && type != QMetaType::QModelIndex && type != QMetaType::VoidStar) + || type == QMetaType::fromType().id(); } ValuesChangedCommand NodeInstanceServer::createValuesChangedCommand(const QList &instanceList) const diff --git a/src/tools/qml2puppet/qml2puppet/instances/nodeinstanceserver.h b/src/tools/qml2puppet/qml2puppet/instances/nodeinstanceserver.h index 65542dedc25..09c87b3af92 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/nodeinstanceserver.h +++ b/src/tools/qml2puppet/qml2puppet/instances/nodeinstanceserver.h @@ -258,6 +258,7 @@ protected: void setRenderTimerInterval(int timerInterval); int renderTimerInterval() const; void setSlowRenderTimerInterval(int timerInterval); + TimerMode timerMode() const { return m_timerMode; } virtual void initializeView() = 0; virtual void initializeAuxiliaryViews(); diff --git a/src/tools/qml2puppet/qml2puppet/instances/nodeinstanceserverdispatcher.cpp b/src/tools/qml2puppet/qml2puppet/instances/nodeinstanceserverdispatcher.cpp index 9815d21ac47..9e3cdac151e 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/nodeinstanceserverdispatcher.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/nodeinstanceserverdispatcher.cpp @@ -6,6 +6,7 @@ #include "qt5bakelightsnodeinstanceserver.h" #include "qt5captureimagenodeinstanceserver.h" #include "qt5capturepreviewnodeinstanceserver.h" +#include "qt5import3dnodeinstanceserver.h" #include "qt5informationnodeinstanceserver.h" #include "qt5rendernodeinstanceserver.h" @@ -173,6 +174,8 @@ std::unique_ptr createNodeInstanceServer( return std::make_unique(nodeInstanceClient); else if (serverName == "bakelightsmode") return std::make_unique(nodeInstanceClient); + else if (serverName == "import3dmode") + return std::make_unique(nodeInstanceClient); return {}; } diff --git a/src/tools/qml2puppet/qml2puppet/instances/objectnodeinstance.cpp b/src/tools/qml2puppet/qml2puppet/instances/objectnodeinstance.cpp index 813619d8167..e2529f42af7 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/objectnodeinstance.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/objectnodeinstance.cpp @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -618,7 +619,8 @@ QVariant ObjectNodeInstance::property(const PropertyName &name) const QQmlProperty property(object(), QString::fromUtf8(name), context()); if (property.property().isEnumType()) { QVariant value = property.read(); - return property.property().enumerator().valueToKey(value.toInt()); + QMetaEnum me = property.property().enumerator(); + return QVariant::fromValue(Enumeration(me.scope(), me.valueToKey(value.toInt()))); } if (property.propertyType() == QVariant::Url) { @@ -627,8 +629,8 @@ QVariant ObjectNodeInstance::property(const PropertyName &name) const return QVariant(); if (url.scheme() == "file") { - int basePathLength = nodeInstanceServer()->fileUrl().toLocalFile().lastIndexOf('/'); - return QUrl(url.toLocalFile().mid(basePathLength + 1)); + QFileInfo fi{nodeInstanceServer()->fileUrl().toLocalFile()}; + return QUrl{fi.absoluteDir().relativeFilePath(url.toLocalFile())}; } } diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5import3dnodeinstanceserver.cpp b/src/tools/qml2puppet/qml2puppet/instances/qt5import3dnodeinstanceserver.cpp new file mode 100644 index 00000000000..5d45a881af5 --- /dev/null +++ b/src/tools/qml2puppet/qml2puppet/instances/qt5import3dnodeinstanceserver.cpp @@ -0,0 +1,205 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +#include "qt5import3dnodeinstanceserver.h" + +#include "createscenecommand.h" +#include "view3dactioncommand.h" + +#include "imagecontainer.h" +#include "nodeinstanceclientinterface.h" +#include "puppettocreatorcommand.h" + +#include +#include +#include +#include + +#include + +#include +#include +#include + +namespace QmlDesigner { + +Qt5Import3dNodeInstanceServer::Qt5Import3dNodeInstanceServer(NodeInstanceClientInterface *nodeInstanceClient) : + Qt5NodeInstanceServer(nodeInstanceClient) +{ + setSlowRenderTimerInterval(100000000); + setRenderTimerInterval(20); + +#ifdef QUICK3D_MODULE + m_generalHelper = new Internal::GeneralHelper(); + QObject::connect(m_generalHelper, &Internal::GeneralHelper::requestRender, this, [this]() { + startRenderTimer(); + }); +#endif +} + +Qt5Import3dNodeInstanceServer::~Qt5Import3dNodeInstanceServer() +{ + cleanup(); +} + +void Qt5Import3dNodeInstanceServer::createScene(const CreateSceneCommand &command) +{ + initializeView(); + registerFonts(command.resourceUrl); + setTranslationLanguage(command.language); + setupScene(command); + startRenderTimer(); +} + +void Qt5Import3dNodeInstanceServer::view3DAction([[maybe_unused]] const View3DActionCommand &command) +{ + switch (command.type()) { + case View3DActionType::Import3dUpdatePreviewImage: { + QObject *obj = rootItem(); + if (obj) { + QSize size = command.value().toSize(); + QQmlProperty wProp(obj, "width", context()); + QQmlProperty hProp(obj, "height", context()); + wProp.write(size.width()); + hProp.write(size.height()); + resizeCanvasToRootItem(); + + startRenderTimer(); + } + break; + } + case View3DActionType::Import3dRotatePreviewModel: { + QObject *obj = rootItem(); + QQmlProperty sceneNodeProp(obj, "sceneNode", context()); + auto sceneNode = sceneNodeProp.read().value(); + if (sceneNode) { + QPointF delta = command.value().toPointF(); + m_generalHelper->orbitCamera(m_view3D->camera(), m_view3D->camera()->eulerRotation(), + m_lookAt, {}, {float(delta.x()), float(delta.y()), 0.f}); + m_keepRendering = true; + startRenderTimer(); + } + break; + } + default: + break; + } +} + +void Qt5Import3dNodeInstanceServer::startRenderTimer() +{ + if (m_keepRendering && timerMode() == TimerMode::NormalTimer) + return; + + NodeInstanceServer::startRenderTimer(); +} + +void Qt5Import3dNodeInstanceServer::cleanup() +{ +#ifdef QUICK3D_MODULE + delete m_previewNode; + delete m_generalHelper; +#endif +} + +void Qt5Import3dNodeInstanceServer::finish() +{ + cleanup(); +} + +void Qt5Import3dNodeInstanceServer::collectItemChangesAndSendChangeCommands() +{ + static bool inFunction = false; + + if (!rootNodeInstance().holdsGraphical()) + return; + + if (!inFunction) { + inFunction = true; + + QQuickDesignerSupport::polishItems(quickWindow()); + + render(); + + inFunction = false; + } +} + +void Qt5Import3dNodeInstanceServer::render() +{ +#ifdef QUICK3D_MODULE + ++m_renderCount; + + if (m_renderCount == 1) { + QObject *obj = rootItem(); + QQmlProperty viewProp(obj, "view3d", context()); + QObject *viewObj = viewProp.read().value(); + m_view3D = qobject_cast(viewObj); + if (m_view3D) { + QQmlProperty sceneModelNameProp(obj, "sceneModelName", context()); + QQmlProperty sceneNodeProp(obj, "sceneNode", context()); + auto sceneNode = sceneNodeProp.read().value(); + if (sceneNode) { + QString sceneModelName = sceneModelNameProp.read().toString(); + QFileInfo fi(fileUrl().toLocalFile()); + QString compPath = fi.absolutePath() + '/' + sceneModelName + ".qml"; + QQmlComponent comp(engine(), compPath, QQmlComponent::PreferSynchronous); + m_previewNode = qobject_cast(comp.create(context())); + if (m_previewNode) { + engine()->setObjectOwnership(m_previewNode, QJSEngine::CppOwnership); + m_previewNode->setParentItem(sceneNode); + m_previewNode->setParent(sceneNode); + } + } + } + } + + // Render scene once to ensure geometries are intialized so bounds calculations work correctly + if (m_renderCount == 2 && m_view3D) { + QVector3D extents; + m_generalHelper->calculateBoundsAndFocusCamera(m_view3D->camera(), m_previewNode, + m_view3D, 1050, false, m_lookAt, extents); + auto getExtentStr = [&extents](int idx) -> QString { + int prec = 0; + float val = extents[idx]; + while (val < 100.f) { + ++prec; + val *= 10.f; + } + // Strip unnecessary zeroes after decimal separator + if (prec > 0) { + QString checkStr = QString::number(extents[idx], 'f', prec); + while (prec > 0 && (checkStr.last(1) == "0" || checkStr.last(1) == ".")) { + --prec; + checkStr.chop(1); + } + } + QString retval = QLocale().toString(extents[idx], 'f', prec); + return retval; + }; + QQmlProperty extentsProp(rootItem(), "extents", context()); + extentsProp.write(tr("Dimensions: %1 x %2 x %3").arg(getExtentStr(0)) + .arg(getExtentStr(1)) + .arg(getExtentStr(2))); + } + + rootNodeInstance().updateDirtyNodeRecursive(); + QImage renderImage = grabWindow(); + + if (m_renderCount >= 2) { + ImageContainer imgContainer(0, renderImage, m_renderCount); + nodeInstanceClient()->handlePuppetToCreatorCommand( + {PuppetToCreatorCommand::Import3DPreviewImage, + QVariant::fromValue(imgContainer)}); + + if (!m_keepRendering) + slowDownRenderTimer(); + + m_keepRendering = false; + } +#else + slowDownRenderTimer(); +#endif +} + +} // namespace QmlDesigner diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5import3dnodeinstanceserver.h b/src/tools/qml2puppet/qml2puppet/instances/qt5import3dnodeinstanceserver.h new file mode 100644 index 00000000000..a68401ef9fd --- /dev/null +++ b/src/tools/qml2puppet/qml2puppet/instances/qt5import3dnodeinstanceserver.h @@ -0,0 +1,48 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "generalhelper.h" +#include "qt5nodeinstanceserver.h" + +QT_BEGIN_NAMESPACE +class QQuick3DNode; +QT_END_NAMESPACE + +namespace QmlDesigner { + +class Qt5Import3dNodeInstanceServer : public Qt5NodeInstanceServer +{ + Q_OBJECT +public: + explicit Qt5Import3dNodeInstanceServer(NodeInstanceClientInterface *nodeInstanceClient); + +public: + virtual ~Qt5Import3dNodeInstanceServer(); + + void createScene(const CreateSceneCommand &command) override; + void view3DAction(const View3DActionCommand &command) override; + + void render(); + +protected: + void collectItemChangesAndSendChangeCommands() override; + void startRenderTimer() override; + +private: + void finish(); + void cleanup(); + + int m_renderCount = 0; + bool m_keepRendering = false; + +#ifdef QUICK3D_MODULE + QQuick3DViewport *m_view3D = nullptr; + Internal::GeneralHelper *m_generalHelper = nullptr; + QQuick3DNode *m_previewNode = nullptr; + QVector3D m_lookAt; +#endif +}; + +} // namespace QmlDesigner diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp index 6cd39a5a85b..9549f59d3f3 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp @@ -41,14 +41,15 @@ #include "changeauxiliarycommand.h" #include "../editor3d/boxgeometry.h" -#include "../editor3d/generalhelper.h" -#include "../editor3d/mousearea3d.h" #include "../editor3d/camerageometry.h" -#include "../editor3d/lightgeometry.h" +#include "../editor3d/generalhelper.h" #include "../editor3d/gridgeometry.h" -#include "../editor3d/selectionboxgeometry.h" -#include "../editor3d/linegeometry.h" #include "../editor3d/icongizmoimageprovider.h" +#include "../editor3d/lightgeometry.h" +#include "../editor3d/linegeometry.h" +#include "../editor3d/lookatgeometry.h" +#include "../editor3d/mousearea3d.h" +#include "../editor3d/selectionboxgeometry.h" #include #include @@ -74,6 +75,7 @@ #include #include #include +#include #include #if defined(QUICK3D_ASSET_UTILS_MODULE) #include @@ -525,6 +527,7 @@ void Qt5InformationNodeInstanceServer::createEditView3D() qmlRegisterType("SelectionBoxGeometry", 1, 0, "SelectionBoxGeometry"); qmlRegisterType("LineGeometry", 1, 0, "LineGeometry"); qmlRegisterType("BoxGeometry", 1, 0, "BoxGeometry"); + qmlRegisterType("LookAtGeometry", 1, 0, "LookAtGeometry"); auto helper = new QmlDesigner::Internal::GeneralHelper(); QObject::connect(helper, &QmlDesigner::Internal::GeneralHelper::toolStateChanged, @@ -979,6 +982,9 @@ void Qt5InformationNodeInstanceServer::handleNode3DDestroyed([[maybe_unused]] QO QMetaObject::invokeMethod(m_editView3DData.rootItem, "releaseParticleEmitterGizmo", Q_ARG(QVariant, objectToVariant(obj))); #endif + } else if (qobject_cast(obj)) { + QMetaObject::invokeMethod(m_editView3DData.rootItem, "releaseReflectionProbeGizmo", + Q_ARG(QVariant, objectToVariant(obj))); } removeNode3D(obj); #endif @@ -1112,6 +1118,10 @@ void Qt5InformationNodeInstanceServer::resolveSceneRoots() Q_ARG(QVariant, objectToVariant(newRoot)), Q_ARG(QVariant, objectToVariant(node))); #endif + } else if (qobject_cast(node)) { + QMetaObject::invokeMethod(m_editView3DData.rootItem, "updateReflectionProbeGizmoScene", + Q_ARG(QVariant, objectToVariant(newRoot)), + Q_ARG(QVariant, objectToVariant(node))); } } ++it; @@ -1599,7 +1609,7 @@ QList Qt5InformationNodeInstanceServer::createInstances( if (m_editView3DSetupDone) { add3DViewPorts(createdInstances); add3DScenes(createdInstances); - createCameraAndLightGizmos(createdInstances); + createGizmos(createdInstances); } render3DEditView(); @@ -1652,13 +1662,14 @@ void Qt5InformationNodeInstanceServer::handleDynamicAddObjectTimeout() m_dynamicObjectConstructors.clear(); } -void Qt5InformationNodeInstanceServer::createCameraAndLightGizmos( +void Qt5InformationNodeInstanceServer::createGizmos( const QList &instanceList) const { QHash cameras; QHash lights; QHash particleSystems; QHash particleEmitters; + QHash reflectionProbes; for (const ServerNodeInstance &instance : instanceList) { if (instance.isSubclassOf("QQuick3DCamera")) { @@ -1671,6 +1682,8 @@ void Qt5InformationNodeInstanceServer::createCameraAndLightGizmos( || instance.isSubclassOf("QQuick3DParticleAttractor")) && !instance.isSubclassOf("QQuick3DParticleTrailEmitter")) { particleEmitters[find3DSceneRoot(instance)] << instance.internalObject(); + } else if (instance.isSubclassOf("QQuick3DReflectionProbe")) { + reflectionProbes[find3DSceneRoot(instance)] << instance.internalObject(); } } @@ -1715,6 +1728,17 @@ void Qt5InformationNodeInstanceServer::createCameraAndLightGizmos( } ++emitterIt; } + + auto refProbeIt = reflectionProbes.constBegin(); + while (refProbeIt != reflectionProbes.constEnd()) { + const auto refProbeObjs = refProbeIt.value(); + for (auto &obj : refProbeObjs) { + QMetaObject::invokeMethod(m_editView3DData.rootItem, "addReflectionProbeGizmo", + Q_ARG(QVariant, objectToVariant(refProbeIt.key())), + Q_ARG(QVariant, objectToVariant(obj))); + } + ++refProbeIt; + } } void Qt5InformationNodeInstanceServer::add3DViewPorts(const QList &instanceList) @@ -1941,6 +1965,8 @@ void Qt5InformationNodeInstanceServer::setup3DEditView( if (toolStates[helper->globalStateId()].contains(helper->lastSceneIdKey())) lastSceneId = toolStates[helper->globalStateId()][helper->lastSceneIdKey()].toString(); } + if (toolStates.contains(helper->projectStateId())) + helper->setLastSceneEnvironmentData(toolStates[helper->projectStateId()][helper->lastSceneEnvKey()].toMap()); } // Find a scene to show @@ -1977,7 +2003,7 @@ void Qt5InformationNodeInstanceServer::setup3DEditView( updateActiveSceneToEditView3D(); - createCameraAndLightGizmos(instanceList); + createGizmos(instanceList); // Queue two renders to make sure icon gizmos update properly render3DEditView(2); @@ -2509,6 +2535,9 @@ void Qt5InformationNodeInstanceServer::view3DAction(const View3DActionCommand &c case View3DActionType::ShowGrid: updatedToolState.insert("showGrid", command.isEnabled()); break; + case View3DActionType::ShowLookAt: + updatedToolState.insert("showLookAt", command.isEnabled()); + break; case View3DActionType::ShowSelectionBox: updatedToolState.insert("showSelectionBox", command.isEnabled()); break; @@ -2585,6 +2614,12 @@ void Qt5InformationNodeInstanceServer::view3DAction(const View3DActionCommand &c case View3DActionType::MaterialOverride: updatedToolState.insert("matOverride", command.value().toList()); break; + case View3DActionType::SetLastSceneEnvData: { + auto helper = qobject_cast(m_3dHelper); + if (helper) + helper->setLastSceneEnvironmentData(command.value().toMap()); + break; + } default: break; diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.h b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.h index 4f7fcd71774..321751cf238 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.h +++ b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.h @@ -92,7 +92,7 @@ private: void create3DPreviewView(); void setup3DEditView(const QList &instanceList, const CreateSceneCommand &command); - void createCameraAndLightGizmos(const QList &instanceList) const; + void createGizmos(const QList &instanceList) const; void add3DViewPorts(const QList &instanceList); void add3DScenes(const QList &instanceList); QObject *findView3DForInstance(const ServerNodeInstance &instance) const; diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5nodeinstanceclientproxy.cpp b/src/tools/qml2puppet/qml2puppet/instances/qt5nodeinstanceclientproxy.cpp index f4d01d28db4..c11899e0ec9 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/qt5nodeinstanceclientproxy.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/qt5nodeinstanceclientproxy.cpp @@ -10,6 +10,7 @@ #include "qt5captureimagenodeinstanceserver.h" #include "qt5capturepreviewnodeinstanceserver.h" #include "qt5informationnodeinstanceserver.h" +#include "qt5import3dnodeinstanceserver.h" #include "qt5previewnodeinstanceserver.h" #include "qt5rendernodeinstanceserver.h" #include "qt5testnodeinstanceserver.h" @@ -70,6 +71,9 @@ Qt5NodeInstanceClientProxy::Qt5NodeInstanceClientProxy(QObject *parent) : } else if (QCoreApplication::arguments().at(2) == QLatin1String("bakelightsmode")) { setNodeInstanceServer(std::make_unique(this)); initializeSocket(); + } else if (QCoreApplication::arguments().at(2) == QLatin1String("import3dmode")) { + setNodeInstanceServer(std::make_unique(this)); + initializeSocket(); } } diff --git a/src/tools/qml2puppet/qml2puppet/qml2puppetmain.cpp b/src/tools/qml2puppet/qml2puppet/qml2puppetmain.cpp index 5896df39e1f..937bb36d8eb 100644 --- a/src/tools/qml2puppet/qml2puppet/qml2puppetmain.cpp +++ b/src/tools/qml2puppet/qml2puppet/qml2puppetmain.cpp @@ -7,6 +7,34 @@ #include "runner/qmlruntime.h" #endif +#include + +inline Q_LOGGING_CATEGORY(deprecated, "qt.tools.qds.deprecated"); +inline Q_LOGGING_CATEGORY(verbose1, "qt.tools.qds.verbose1"); +inline Q_LOGGING_CATEGORY(verbose2, "qt.tools.qds.verbose2"); + +void registerMessageHandler( + QtMsgType type, [[maybe_unused]] const QMessageLogContext &context, const QString &msg) +{ + auto tPrinter = [&](const QString &msgPrefix) { + fprintf( + stderr, "%s: %s\n", msgPrefix.toLocal8Bit().constData(), msg.toLocal8Bit().constData()); + }; + + if (type == QtDebugMsg) + tPrinter("Debug"); + else if (type == QtInfoMsg) + tPrinter("Info"); + else if (type == QtWarningMsg) + tPrinter("Warning"); + else if (type == QtCriticalMsg) + tPrinter("Critical"); + else if (type == QtFatalMsg) { + tPrinter("Fatal"); + abort(); + } +} + auto getQmlRunner(int &argc, char **argv) { #ifdef ENABLE_INTERNAL_QML_RUNTIME @@ -24,7 +52,6 @@ auto getQmlRunner(int &argc, char **argv) int main(int argc, char *argv[]) { - QDSMeta::Logging::registerMessageHandler(); - QDSMeta::AppInfo::registerAppInfo("Qml2Puppet"); + qInstallMessageHandler(registerMessageHandler); return getQmlRunner(argc, argv)->run(); } diff --git a/src/tools/qml2puppet/qml2puppet/qmlbase.h b/src/tools/qml2puppet/qml2puppet/qmlbase.h index ae8995f4893..45be0cae71d 100644 --- a/src/tools/qml2puppet/qml2puppet/qmlbase.h +++ b/src/tools/qml2puppet/qml2puppet/qmlbase.h @@ -3,27 +3,12 @@ #pragma once -#include -#include -#include -#include - -#include -#include -#include -#include - +#include #include +#include -#include -#include - -#include - -#include "appmetadata.h" #include -#include class QmlBase : public QObject { Q_OBJECT @@ -44,7 +29,6 @@ public: #ifdef ENABLE_INTERNAL_QML_RUNTIME m_argParser.addOption({"qml-runtime", "Run QML Runtime"}); #endif - m_argParser.addOption({"appinfo", "Print build information"}); m_argParser.addOption({"test", "Run test mode"}); } @@ -92,7 +76,6 @@ private: void initParser() { QCommandLineOption optHelp = m_argParser.addHelpOption(); - QCommandLineOption optVers = m_argParser.addVersionOption(); if (!m_argParser.parse(m_coreApp->arguments())) { std::cout << "Error: " << m_argParser.errorText().toStdString() << std::endl; @@ -103,12 +86,8 @@ private: std::cout << std::endl; m_argParser.showHelp(1); - } else if (m_argParser.isSet(optVers)) { - m_argParser.showVersion(); } else if (m_argParser.isSet(optHelp)) { m_argParser.showHelp(0); - } else if (m_argParser.isSet("appinfo")) { - QDSMeta::AppInfo::printAppInfo(); } else if (m_argParser.isSet("test")) { exit(startTestMode()); } diff --git a/src/tools/qml2puppet/qml2puppet/qmlpuppet.cpp b/src/tools/qml2puppet/qml2puppet/qmlpuppet.cpp index 86dce8cb90d..332def504ee 100644 --- a/src/tools/qml2puppet/qml2puppet/qmlpuppet.cpp +++ b/src/tools/qml2puppet/qml2puppet/qmlpuppet.cpp @@ -10,7 +10,6 @@ #include #include - #include #include diff --git a/src/tools/qml2puppet/qml2puppet/runner/qmlruntime.cpp b/src/tools/qml2puppet/qml2puppet/runner/qmlruntime.cpp index 4435d0e9f42..2876339a903 100644 --- a/src/tools/qml2puppet/qml2puppet/runner/qmlruntime.cpp +++ b/src/tools/qml2puppet/qml2puppet/runner/qmlruntime.cpp @@ -13,6 +13,10 @@ #include #include #include +#include +#include +#include +#include #define FILE_OPEN_EVENT_WAIT_TIME 3000 // ms #define QSL QStringLiteral @@ -242,12 +246,12 @@ void QmlRuntime::initQmlRunner() if (translator.load(translationFile)) { m_coreApp->installTranslator(&translator); if (m_verboseMode) - qInfo() << "qml: Loaded translation file %s\n", - qPrintable(QDir::toNativeSeparators(translationFile)); + qInfo() << "qml: Loaded translation file " + << qPrintable(QDir::toNativeSeparators(translationFile)); } else { if (!m_quietMode) - qInfo() << "qml: Could not load the translation file %s\n", - qPrintable(QDir::toNativeSeparators(translationFile)); + qInfo() << "qml: Could not load the translation file " + << qPrintable(QDir::toNativeSeparators(translationFile)); } } #else @@ -278,7 +282,7 @@ void QmlRuntime::initQmlRunner() for (const QString &path : std::as_const(files)) { QUrl url = QUrl::fromUserInput(path, QDir::currentPath(), QUrl::AssumeLocalFile); if (m_verboseMode) - qInfo() << "qml: loading %s\n", qPrintable(url.toString()); + qInfo() << "qml: loading " << qPrintable(url.toString()); m_qmlEngine->load(url); } @@ -318,8 +322,8 @@ void QmlRuntime::loadConf(const QString &override, bool quiet) // Terminates app else fi.setFile(override); if (!fi.exists()) { - qCritical() << "qml: Couldn't find required configuration file: %s\n", - qPrintable(QDir::toNativeSeparators(fi.absoluteFilePath())); + qCritical() << "qml: Couldn't find required configuration file:" + << qPrintable(QDir::toNativeSeparators(fi.absoluteFilePath())); exit(1); } settingsUrl = QQmlImports::urlFromLocalFileOrQrcOrUrl(fi.absoluteFilePath()); @@ -327,13 +331,14 @@ void QmlRuntime::loadConf(const QString &override, bool quiet) // Terminates app } if (!quiet) { - qInfo() << "qml: %s\n", QLibraryInfo::build(); + qInfo() << "qml:" << QLibraryInfo::build(); if (builtIn) { - qInfo() << "qml: Using built-in configuration: %s\n", - qPrintable(override.isEmpty() ? defaultFileName : override); + qInfo() << "qml: Using built-in configuration:" + << qPrintable(override.isEmpty() ? defaultFileName : override); } else { - qInfo() << "qml: Using configuration: %s\n", - qPrintable(settingsUrl.isLocalFile() + qInfo() << "qml: Using configuration:" + << qPrintable( + settingsUrl.isLocalFile() ? QDir::toNativeSeparators(settingsUrl.toLocalFile()) : settingsUrl.toString()); } @@ -345,18 +350,19 @@ void QmlRuntime::loadConf(const QString &override, bool quiet) // Terminates app m_conf.reset(qobject_cast(c2.create())); if (!m_conf) { - qCritical() << "qml: Error loading configuration file: %s\n", qPrintable(c2.errorString()); + qCritical() << "qml: Error loading configuration file:" << qPrintable(c2.errorString()); exit(1); } } void QmlRuntime::listConfFiles() { + qDebug() << "qml: Built-in configurations:"; const QDir confResourceDir(m_confResourcePath); - qInfo() << "%s\n", qPrintable(QCoreApplication::translate("main", "Built-in configurations:")); + qInfo() << qPrintable(QCoreApplication::translate("main", "Built-in configurations:")); for (const QFileInfo &fi : confResourceDir.entryInfoList(QDir::Files)) - qInfo() << " %s\n", qPrintable(fi.baseName()); - qInfo() << "%s\n", qPrintable(QCoreApplication::translate("main", "Other configurations:")); + qInfo() << qPrintable(fi.baseName()); + qInfo() << qPrintable(QCoreApplication::translate("main", "Other configurations:")); bool foundOther = false; const QStringList otherLocations = QStandardPaths::standardLocations( QStandardPaths::AppConfigLocation); @@ -365,16 +371,16 @@ void QmlRuntime::listConfFiles() for (const QFileInfo &fi : confDir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot)) { foundOther = true; if (m_verboseMode) - qInfo() << " %s\n", qPrintable(fi.absoluteFilePath()); + qInfo() << qPrintable(fi.absoluteFilePath()); else - qInfo() << " %s\n", qPrintable(fi.baseName()); + qInfo() << qPrintable(fi.baseName()); } } if (!foundOther) - qInfo() << " %s\n", qPrintable(QCoreApplication::translate("main", "none")); + qInfo() << qPrintable(QCoreApplication::translate("main", "none")); if (m_verboseMode) { - qInfo() << "%s\n", qPrintable(QCoreApplication::translate("main", "Checked in:")); + qInfo() << qPrintable(QCoreApplication::translate("main", "Checked in:")); for (const auto &confDirPath : otherLocations) - qInfo() << " %s\n", qPrintable(confDirPath); + qInfo() << qPrintable(confDirPath); } } diff --git a/tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp b/tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp index bd7071f054c..d37b639cd0c 100644 --- a/tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp +++ b/tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp @@ -149,6 +149,7 @@ public: QString defaultPuppetToplevelBuildDirectory() const override { return {}; } QString qmlPuppetFallbackDirectory() const override { return {}; } QUrl projectUrl() const override { return {}; } + QString projectName() const override { return {}; } void parseItemLibraryDescriptions() override {} const QmlDesigner::DesignerSettings &designerSettings() const override { return settings; } void undoOnCurrentDesignDocument() override {} diff --git a/tests/unit/tests/matchers/projectstorage-matcher.h b/tests/unit/tests/matchers/projectstorage-matcher.h index 02861d7eea3..56b4ad9d6ae 100644 --- a/tests/unit/tests/matchers/projectstorage-matcher.h +++ b/tests/unit/tests/matchers/projectstorage-matcher.h @@ -20,6 +20,7 @@ MATCHER_P2(IsTypeHint, template auto IsItemLibraryEntry(QmlDesigner::TypeId typeId, + Utils::SmallStringView typeName, Utils::SmallStringView name, Utils::SmallStringView iconPath, Utils::SmallStringView category, @@ -31,6 +32,7 @@ auto IsItemLibraryEntry(QmlDesigner::TypeId typeId, { using QmlDesigner::Storage::Info::ItemLibraryEntry; return AllOf(Field("typeId", &ItemLibraryEntry::typeId, typeId), + Field("typeName", &ItemLibraryEntry::typeName, typeName), Field("name", &ItemLibraryEntry::name, name), Field("iconPath", &ItemLibraryEntry::iconPath, iconPath), Field("category", &ItemLibraryEntry::category, category), @@ -66,7 +68,7 @@ auto IsTypeAnnotation(QmlDesigner::SourceId sourceId, { using QmlDesigner::Storage::Synchronization::TypeAnnotation; return AllOf(Field("sourceId", &TypeAnnotation::sourceId, sourceId), - Field("sourceId", &TypeAnnotation::directorySourceId, directorySourceId), + Field("directory sourceId", &TypeAnnotation::directorySourceId, directorySourceId), Field("typeName", &TypeAnnotation::typeName, typeName), Field("moduleId", &TypeAnnotation::moduleId, moduleId), Field("iconPath", &TypeAnnotation::iconPath, iconPath), diff --git a/tests/unit/tests/mocks/CMakeLists.txt b/tests/unit/tests/mocks/CMakeLists.txt index d2090432626..0fdfa639c09 100644 --- a/tests/unit/tests/mocks/CMakeLists.txt +++ b/tests/unit/tests/mocks/CMakeLists.txt @@ -22,6 +22,7 @@ add_qtc_library(TestMocks OBJECT mocktimestampprovider.h modelresourcemanagementmock.h propertycomponentgeneratormock.h + projectstorageerrornotifiermock.h projectstoragemock.cpp projectstoragemock.h projectstorageobservermock.h diff --git a/tests/unit/tests/mocks/externaldependenciesmock.h b/tests/unit/tests/mocks/externaldependenciesmock.h index c4cfe6cd3b5..df70a7fcdb3 100644 --- a/tests/unit/tests/mocks/externaldependenciesmock.h +++ b/tests/unit/tests/mocks/externaldependenciesmock.h @@ -15,6 +15,7 @@ public: MOCK_METHOD(QString, qmlPuppetFallbackDirectory, (), (const, override)); MOCK_METHOD(QString, defaultPuppetToplevelBuildDirectory, (), (const, override)); MOCK_METHOD(QUrl, projectUrl, (), (const, override)); + MOCK_METHOD(QString, projectName, (), (const, override)); MOCK_METHOD(QString, currentProjectDirPath, (), (const, override)); MOCK_METHOD(QUrl, currentResourcePath, (), (const, override)); MOCK_METHOD(void, parseItemLibraryDescriptions, (), (override)); diff --git a/tests/unit/tests/mocks/filesystemmock.h b/tests/unit/tests/mocks/filesystemmock.h index cb1d4df4bc4..f8544e509f6 100644 --- a/tests/unit/tests/mocks/filesystemmock.h +++ b/tests/unit/tests/mocks/filesystemmock.h @@ -20,4 +20,5 @@ public: MOCK_METHOD(QmlDesigner::FileStatus, fileStatus, (QmlDesigner::SourceId sourceId), (const, override)); MOCK_METHOD(void, remove, (const QmlDesigner::SourceIds &sourceIds), (override)); MOCK_METHOD(QString, contentAsQString, (const QString &filePath), (const, override)); + MOCK_METHOD(QStringList, subdirectories, (const QString &directoryPath), (const, override)); }; diff --git a/tests/unit/tests/mocks/projectstorageerrornotifiermock.h b/tests/unit/tests/mocks/projectstorageerrornotifiermock.h new file mode 100644 index 00000000000..730c70a66ab --- /dev/null +++ b/tests/unit/tests/mocks/projectstorageerrornotifiermock.h @@ -0,0 +1,17 @@ +// 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/googletest.h" + +#include + +class ProjectStorageErrorNotifierMock : public QmlDesigner::ProjectStorageErrorNotifierInterface +{ +public: + MOCK_METHOD(void, + typeNameCannotBeResolved, + (Utils::SmallStringView typeName, QmlDesigner::SourceId souceId), + (override)); +}; diff --git a/tests/unit/tests/mocks/projectstoragemock.cpp b/tests/unit/tests/mocks/projectstoragemock.cpp index 6d5304879e6..d4a28d1ae6f 100644 --- a/tests/unit/tests/mocks/projectstoragemock.cpp +++ b/tests/unit/tests/mocks/projectstoragemock.cpp @@ -12,9 +12,10 @@ using QmlDesigner::ImportId; using QmlDesigner::ModuleId; using QmlDesigner::PropertyDeclarationId; using QmlDesigner::SourceId; +using QmlDesigner::Storage::ModuleKind; +using QmlDesigner::Storage::PropertyDeclarationTraits; using QmlDesigner::TypeId; using QmlDesigner::TypeIds; -using QmlDesigner::Storage::PropertyDeclarationTraits; namespace Storage = QmlDesigner::Storage; @@ -41,18 +42,20 @@ void setupIsBasedOn(ProjectStorageMock &mock) } // namespace -ModuleId ProjectStorageMock::createModule(Utils::SmallStringView moduleName) +ModuleId ProjectStorageMock::createModule(Utils::SmallStringView moduleName, + QmlDesigner::Storage::ModuleKind moduleKind) { - if (auto id = moduleId(moduleName)) { + if (auto id = moduleId(moduleName, moduleKind)) { return id; } static ModuleId moduleId; incrementBasicId(moduleId); - ON_CALL(*this, moduleId(Eq(moduleName))).WillByDefault(Return(moduleId)); - ON_CALL(*this, moduleName(Eq(moduleId))).WillByDefault(Return(moduleName)); - ON_CALL(*this, fetchModuleIdUnguarded(Eq(moduleName))).WillByDefault(Return(moduleId)); + ON_CALL(*this, moduleId(Eq(moduleName), Eq(moduleKind))).WillByDefault(Return(moduleId)); + ON_CALL(*this, module(Eq(moduleId))) + .WillByDefault(Return(QmlDesigner::Storage::Module{moduleName, moduleKind})); + ON_CALL(*this, fetchModuleIdUnguarded(Eq(moduleName), Eq(moduleKind))).WillByDefault(Return(moduleId)); return moduleId; } @@ -385,11 +388,11 @@ void ProjectStorageMock::setupQtQuick() { setupIsBasedOn(*this); - auto qmlModuleId = createModule("QML"); - auto qmlNativeModuleId = createModule("QML-cppnative"); - auto qtQmlModelsModuleId = createModule("QtQml.Models"); - auto qtQuickModuleId = createModule("QtQuick"); - auto qtQuickNativeModuleId = createModule("QtQuick-cppnative"); + auto qmlModuleId = createModule("QML", ModuleKind::QmlLibrary); + auto qmlNativeModuleId = createModule("QML", ModuleKind::CppLibrary); + auto qtQmlModelsModuleId = createModule("QtQml.Models", ModuleKind::QmlLibrary); + auto qtQuickModuleId = createModule("QtQuick", ModuleKind::QmlLibrary); + auto qtQuickNativeModuleId = createModule("QtQuick", ModuleKind::CppLibrary); auto boolId = createValue(qmlModuleId, "bool"); auto intId = createValue(qmlModuleId, "int"); @@ -463,11 +466,11 @@ void ProjectStorageMock::setupQtQuick() {qtObjectId}); createObject(qtQuickModuleId, "PropertyChanges", {qtObjectId, stateOperationsId}); - auto qtQuickTimelineModuleId = createModule("QtQuick.Timeline"); + auto qtQuickTimelineModuleId = createModule("QtQuick.Timeline", ModuleKind::QmlLibrary); createObject(qtQuickTimelineModuleId, "KeyframeGroup", {qtObjectId}); createObject(qtQuickTimelineModuleId, "Keyframe", {qtObjectId}); - auto flowViewModuleId = createModule("FlowView"); + auto flowViewModuleId = createModule("FlowView", ModuleKind::QmlLibrary); createObject(flowViewModuleId, "FlowActionArea", "data", @@ -492,12 +495,12 @@ void ProjectStorageMock::setupQtQuick() void ProjectStorageMock::setupQtQuickImportedTypeNameIds(QmlDesigner::SourceId sourceId) { - auto qmlModuleId = moduleId("QML"); - auto qtQmlModelsModuleId = moduleId("QtQml.Models"); - auto qtQuickModuleId = moduleId("QtQuick"); - auto qtQuickNativeModuleId = moduleId("QtQuick-cppnative"); - auto qtQuickTimelineModuleId = moduleId("QtQuick.Timeline"); - auto flowViewModuleId = moduleId("FlowView"); + auto qmlModuleId = moduleId("QML", ModuleKind::QmlLibrary); + auto qtQmlModelsModuleId = moduleId("QtQml.Models", ModuleKind::QmlLibrary); + auto qtQuickModuleId = moduleId("QtQuick", ModuleKind::QmlLibrary); + auto qtQuickNativeModuleId = moduleId("QtQuick", ModuleKind::CppLibrary); + auto qtQuickTimelineModuleId = moduleId("QtQuick.Timeline", ModuleKind::QmlLibrary); + auto flowViewModuleId = moduleId("FlowView", ModuleKind::QmlLibrary); createImportedTypeNameId(sourceId, "int", qmlModuleId); createImportedTypeNameId(sourceId, "QtObject", qmlModuleId); diff --git a/tests/unit/tests/mocks/projectstoragemock.h b/tests/unit/tests/mocks/projectstoragemock.h index 8aa5979ddbe..8d9c3381b2e 100644 --- a/tests/unit/tests/mocks/projectstoragemock.h +++ b/tests/unit/tests/mocks/projectstoragemock.h @@ -23,7 +23,8 @@ public: void setupQtQuickImportedTypeNameIds(QmlDesigner::SourceId sourceId); void setupCommonTypeCache(); - QmlDesigner::ModuleId createModule(Utils::SmallStringView moduleName); + QmlDesigner::ModuleId createModule(Utils::SmallStringView moduleName, + QmlDesigner::Storage::ModuleKind moduleKind); QmlDesigner::ImportedTypeNameId createImportedTypeNameId(QmlDesigner::SourceId sourceId, Utils::SmallStringView typeName, @@ -126,8 +127,11 @@ public: MOCK_METHOD(void, addObserver, (QmlDesigner::ProjectStorageObserver *), (override)); MOCK_METHOD(void, removeObserver, (QmlDesigner::ProjectStorageObserver *), (override)); - MOCK_METHOD(QmlDesigner::ModuleId, moduleId, (::Utils::SmallStringView), (const, override)); - MOCK_METHOD(Utils::SmallString, moduleName, (QmlDesigner::ModuleId), (const, override)); + MOCK_METHOD(QmlDesigner::ModuleId, + moduleId, + (::Utils::SmallStringView, QmlDesigner::Storage::ModuleKind moduleKind), + (const, override)); + MOCK_METHOD(QmlDesigner::Storage::Module, module, (QmlDesigner::ModuleId), (const, override)); MOCK_METHOD(std::optional, propertyDeclaration, @@ -296,13 +300,23 @@ public: (QmlDesigner::SourceId sourceId), (const, override)); - MOCK_METHOD(QmlDesigner::Storage::Synchronization::ProjectDatas, - fetchProjectDatas, + MOCK_METHOD(QmlDesigner::Storage::Synchronization::DirectoryInfos, + fetchDirectoryInfos, (QmlDesigner::SourceId sourceId), (const, override)); - MOCK_METHOD(std::optional, - fetchProjectData, + MOCK_METHOD(QmlDesigner::Storage::Synchronization::DirectoryInfos, + fetchDirectoryInfos, + (QmlDesigner::SourceId sourceId, QmlDesigner::Storage::Synchronization::FileType), + (const, override)); + + MOCK_METHOD(QmlDesigner::SmallSourceIds<32>, + fetchSubdirectorySourceIds, + (QmlDesigner::SourceId sourceId), + (const, override)); + + MOCK_METHOD(std::optional, + fetchDirectoryInfo, (QmlDesigner::SourceId sourceId), (const, override)); @@ -337,7 +351,7 @@ public: (const, override)); MOCK_METHOD(QmlDesigner::ModuleId, fetchModuleIdUnguarded, - (Utils::SmallStringView name), + (Utils::SmallStringView name, QmlDesigner::Storage::ModuleKind), (const, override)); MOCK_METHOD(QmlDesigner::TypeId, fetchTypeIdByModuleIdAndExportedName, diff --git a/tests/unit/tests/mocks/qmltypesparsermock.h b/tests/unit/tests/mocks/qmltypesparsermock.h index e3fa1ca6053..0f57c634d07 100644 --- a/tests/unit/tests/mocks/qmltypesparsermock.h +++ b/tests/unit/tests/mocks/qmltypesparsermock.h @@ -15,6 +15,6 @@ public: (const QString &sourceContent, QmlDesigner::Storage::Imports &imports, QmlDesigner::Storage::Synchronization::Types &types, - const QmlDesigner::Storage::Synchronization::ProjectData &projectData), + const QmlDesigner::Storage::Synchronization::DirectoryInfo &directoryInfo), (override)); }; diff --git a/tests/unit/tests/printers/gtest-creator-printing.cpp b/tests/unit/tests/printers/gtest-creator-printing.cpp index 6bc78e99367..8ca65f4526e 100644 --- a/tests/unit/tests/printers/gtest-creator-printing.cpp +++ b/tests/unit/tests/printers/gtest-creator-printing.cpp @@ -695,10 +695,10 @@ std::ostream &operator<<(std::ostream &out, const ItemLibraryProperty &property) std::ostream &operator<<(std::ostream &out, const ItemLibraryEntry &entry) { - return out << R"((")" << entry.name << R"(", ")" << entry.iconPath << R"(", ")" - << entry.category << R"(", ")" << entry.import << R"(", ")" << entry.toolTip - << R"(", ")" << entry.templatePath << R"(", )" << entry.properties << ", " - << entry.extraFilePaths << ")"; + return out << R"((")" << entry.typeName << R"(", ")" << entry.name << R"(", ")" + << entry.iconPath << R"(", ")" << entry.category << R"(", ")" << entry.import + << R"(", ")" << entry.toolTip << R"(", ")" << entry.templatePath << R"(", )" + << entry.properties << ", " << entry.extraFilePaths << ")"; } } // namespace Storage::Info @@ -754,6 +754,8 @@ const char *fileTypeToText(FileType fileType) return "QmlDocument"; case FileType::QmlTypes: return "QmlTypes"; + case FileType::Directory: + return "Directory"; } return ""; @@ -791,8 +793,8 @@ std::ostream &operator<<(std::ostream &out, const SynchronizationPackage &packag << ", updatedSourceIds: " << package.updatedSourceIds << ", fileStatuses: " << package.fileStatuses << ", updatedFileStatusSourceIds: " << package.updatedFileStatusSourceIds - << ", updatedProjectSourceIds: " << package.updatedProjectSourceIds - << ", projectDatas: " << package.projectDatas + << ", updatedDirectoryInfoSourceIds: " << package.updatedDirectoryInfoSourceIds + << ", directoryInfos: " << package.directoryInfos << ", propertyEditorQmlPaths: " << package.propertyEditorQmlPaths << ", updatedPropertyEditorQmlPathSourceIds: " << package.updatedPropertyEditorQmlPathSourceIds @@ -801,9 +803,9 @@ std::ostream &operator<<(std::ostream &out, const SynchronizationPackage &packag << ")"; } -std::ostream &operator<<(std::ostream &out, const ProjectData &data) +std::ostream &operator<<(std::ostream &out, const DirectoryInfo &data) { - return out << "(" << data.projectSourceId << ", " << data.sourceId << ", " << data.moduleId + return out << "(" << data.directorySourceId << ", " << data.sourceId << ", " << data.moduleId << ", " << data.fileType << ")"; } @@ -831,8 +833,9 @@ std::ostream &operator<<(std::ostream &out, const Type &type) { using std::operator<<; using Utils::operator<<; - return out << "( typename: \"" << type.typeName << "\", prototype: " << type.prototype << ", " - << type.prototypeId << ", " << type.traits << ", source: " << type.sourceId + return out << "( typename: \"" << type.typeName << "\", prototype: {\"" << type.prototype + << "\", " << type.prototypeId << "}, " << "\", extension: {\"" << type.extension + << "\", " << type.extensionId << "}, " << type.traits << ", source: " << type.sourceId << ", exports: " << type.exportedTypes << ", properties: " << type.propertyDeclarations << ", functions: " << type.functionDeclarations << ", signals: " << type.signalDeclarations << ", changeLevel: " << type.changeLevel diff --git a/tests/unit/tests/printers/gtest-creator-printing.h b/tests/unit/tests/printers/gtest-creator-printing.h index 8d0c77888ec..2444b9d98b5 100644 --- a/tests/unit/tests/printers/gtest-creator-printing.h +++ b/tests/unit/tests/printers/gtest-creator-printing.h @@ -207,7 +207,7 @@ class EnumeratorDeclaration; enum class ImportKind : char; enum class IsAutoVersion : char; enum class IsQualified : int; -class ProjectData; +class DirectoryInfo; class SynchronizationPackage; enum class FileType : char; enum class ChangeLevel : char; @@ -227,7 +227,7 @@ std::ostream &operator<<(std::ostream &out, const EnumerationDeclaration &enumer std::ostream &operator<<(std::ostream &out, const EnumeratorDeclaration &enumeratorDeclaration); std::ostream &operator<<(std::ostream &out, const ImportKind &importKind); std::ostream &operator<<(std::ostream &out, IsQualified isQualified); -std::ostream &operator<<(std::ostream &out, const ProjectData &data); +std::ostream &operator<<(std::ostream &out, const DirectoryInfo &data); std::ostream &operator<<(std::ostream &out, const SynchronizationPackage &package); std::ostream &operator<<(std::ostream &out, FileType fileType); std::ostream &operator<<(std::ostream &out, ChangeLevel changeLevel); diff --git a/tests/unit/tests/testdesignercore/CMakeLists.txt b/tests/unit/tests/testdesignercore/CMakeLists.txt index 0bdf3452d6a..c446af81a35 100644 --- a/tests/unit/tests/testdesignercore/CMakeLists.txt +++ b/tests/unit/tests/testdesignercore/CMakeLists.txt @@ -125,6 +125,8 @@ add_qtc_library(TestDesignerCore OBJECT projectstorage/projectstorageinterface.h projectstorage/projectstorageobserver.h projectstorage/projectstorage.cpp projectstorage/projectstorage.h + projectstorage/projectstorageerrornotifierinterface.h + projectstorage/projectstorageerrornotifier.cpp projectstorage/projectstorageerrornotifier.h projectstorage/projectstoragepathwatcher.h projectstorage/projectstoragepathwatcherinterface.h projectstorage/projectstoragepathwatchernotifierinterface.h @@ -147,6 +149,8 @@ add_qtc_library(TestDesignerCore OBJECT tracing/qmldesignertracing.cpp tracing/qmldesignertracing.h rewritertransaction.cpp rewritertransaction.h + uniquename.cpp + uniquename.h ) extend_qtc_library(TestDesignerCore diff --git a/tests/unit/tests/unittests/componentcore/propertycomponentgenerator-test.cpp b/tests/unit/tests/unittests/componentcore/propertycomponentgenerator-test.cpp index 557d45e6940..b36966dda16 100644 --- a/tests/unit/tests/unittests/componentcore/propertycomponentgenerator-test.cpp +++ b/tests/unit/tests/unittests/componentcore/propertycomponentgenerator-test.cpp @@ -11,6 +11,7 @@ #include using namespace Qt::StringLiterals; +using QmlDesigner::Storage::ModuleKind; namespace QmlDesigner { @@ -182,7 +183,7 @@ protected: resourceManagementMock)}; QmlDesigner::PropertyComponentGenerator generator{QString{sourcesPath}, &model}; QmlDesigner::NodeMetaInfo itemMetaInfo = model.qtQuickItemMetaInfo(); - QmlDesigner::ModuleId qmlModuleId = projectStorageMock.createModule("QML"); + QmlDesigner::ModuleId qmlModuleId = projectStorageMock.createModule("QML", ModuleKind::QmlLibrary); }; TEST_F(PropertyComponentGenerator, @@ -345,7 +346,8 @@ TEST_F(PropertyComponentGenerator, after_refresh_meta_infos_type_was_deleted) auto xProperty = itemMetaInfo.property("x"); auto doubleMetaInfo = model.doubleMetaInfo(); projectStorageMock.removeExportedTypeName(doubleMetaInfo.id(), - projectStorageMock.createModule("QML"), + projectStorageMock.createModule("QML", + ModuleKind::QmlLibrary), "real"); generator.refreshMetaInfos({doubleMetaInfo.id()}); @@ -359,11 +361,13 @@ TEST_F(PropertyComponentGenerator, after_refresh_meta_infos_type_was_added) auto xProperty = itemMetaInfo.property("x"); auto doubleMetaInfo = model.doubleMetaInfo(); projectStorageMock.removeExportedTypeName(doubleMetaInfo.id(), - projectStorageMock.createModule("QML"), + projectStorageMock.createModule("QML", + ModuleKind::QmlLibrary), "real"); generator.refreshMetaInfos({doubleMetaInfo.id()}); projectStorageMock.addExportedTypeName(doubleMetaInfo.id(), - projectStorageMock.createModule("QML"), + projectStorageMock.createModule("QML", + ModuleKind::QmlLibrary), "real"); generator.refreshMetaInfos({}); diff --git a/tests/unit/tests/unittests/componentcore/propertyeditorcomponentgenerator-test.cpp b/tests/unit/tests/unittests/componentcore/propertyeditorcomponentgenerator-test.cpp index d2f2143a737..398c54bfad4 100644 --- a/tests/unit/tests/unittests/componentcore/propertyeditorcomponentgenerator-test.cpp +++ b/tests/unit/tests/unittests/componentcore/propertyeditorcomponentgenerator-test.cpp @@ -13,6 +13,7 @@ namespace { using BasicProperty = QmlDesigner::PropertyComponentGenerator::BasicProperty; using ComplexProperty = QmlDesigner::PropertyComponentGenerator::ComplexProperty; using QmlDesigner::PropertyMetaInfo; +using QmlDesigner::Storage::ModuleKind; class PropertyEditorComponentGenerator : public ::testing::Test { @@ -86,7 +87,8 @@ protected: NiceMock projectStorageMock{sourceId}; NiceMock propertyGeneratorMock; QmlDesigner::PropertyEditorComponentGenerator generator{propertyGeneratorMock}; - QmlDesigner::ModuleId qtQuickModuleId = projectStorageMock.createModule("QtQuick"); + QmlDesigner::ModuleId qtQuickModuleId = projectStorageMock.createModule("QtQuick", + ModuleKind::QmlLibrary); QmlDesigner::NodeMetaInfo fooTypeInfo = createType("Foo"); QmlDesigner::TypeId dummyTypeId = projectStorageMock.commonTypeCache().builtinTypeId(); }; diff --git a/tests/unit/tests/unittests/metainfo/nodemetainfo-test.cpp b/tests/unit/tests/unittests/metainfo/nodemetainfo-test.cpp index af8c4bd2209..c96def356ab 100644 --- a/tests/unit/tests/unittests/metainfo/nodemetainfo-test.cpp +++ b/tests/unit/tests/unittests/metainfo/nodemetainfo-test.cpp @@ -17,6 +17,7 @@ namespace { using QmlDesigner::FlagIs; using QmlDesigner::ModelNode; using QmlDesigner::ModelNodes; +using QmlDesigner::Storage::ModuleKind; using QmlDesigner::Storage::TypeTraits; using QmlDesigner::Storage::TypeTraitsKind; @@ -68,9 +69,10 @@ protected: } QmlDesigner::NodeMetaInfo createDerivedDummyMetaInfo(Utils::SmallStringView moduleName, + ModuleKind moduleKind, Utils::SmallStringView typeName) { - auto moduleId = projectStorageMock.createModule(moduleName); + auto moduleId = projectStorageMock.createModule(moduleName, moduleKind); auto typeId = projectStorageMock.createType(moduleId, typeName, {}); return createDerivedDummyMetaInfo(typeId); @@ -86,10 +88,11 @@ protected: } QmlDesigner::NodeMetaInfo createMetaInfo(Utils::SmallStringView moduleName, + ModuleKind moduleKind, Utils::SmallStringView typeName, QmlDesigner::Storage::TypeTraits typeTraits = {}) { - auto moduleId = projectStorageMock.createModule(moduleName); + auto moduleId = projectStorageMock.createModule(moduleName, moduleKind); auto typeId = projectStorageMock.createType(moduleId, typeName, typeTraits); return QmlDesigner::NodeMetaInfo{typeId, &projectStorageMock}; @@ -109,8 +112,9 @@ protected: ModelNode object = model.createModelNode("QtObject"); QmlDesigner::NodeMetaInfo itemMetaInfo = item.metaInfo(); QmlDesigner::NodeMetaInfo objectMetaInfo = object.metaInfo(); - QmlDesigner::ModuleId qmlModuleId = projectStorageMock.createModule("QML"); - QmlDesigner::ModuleId qtQuickModuleId = projectStorageMock.createModule("QtQuick"); + QmlDesigner::ModuleId qmlModuleId = projectStorageMock.createModule("QML", ModuleKind::QmlLibrary); + QmlDesigner::ModuleId qtQuickModuleId = projectStorageMock.createModule("QtQuick", + ModuleKind::QmlLibrary); QmlDesigner::TypeId intTypeId = projectStorageMock.typeId(qmlModuleId, "int", QmlDesigner::Storage::Version{}); @@ -215,7 +219,7 @@ TEST_F(NodeMetaInfo, invalid_is_not_file_component) TEST_F(NodeMetaInfo, component_is_file_component) { - auto moduleId = projectStorageMock.createModule("/path/to/project"); + auto moduleId = projectStorageMock.createModule("/path/to/project", ModuleKind::PathLibrary); TypeTraits traits{TypeTraitsKind::Reference}; traits.isFileComponent = true; auto typeId = projectStorageMock.createType(moduleId, "Foo", traits); @@ -228,7 +232,7 @@ TEST_F(NodeMetaInfo, component_is_file_component) TEST_F(NodeMetaInfo, is_project_component) { - auto moduleId = projectStorageMock.createModule("/path/to/project"); + auto moduleId = projectStorageMock.createModule("/path/to/project", ModuleKind::PathLibrary); TypeTraits traits{TypeTraitsKind::Reference}; traits.isProjectComponent = true; auto typeId = projectStorageMock.createType(moduleId, "Foo", traits); @@ -242,7 +246,7 @@ TEST_F(NodeMetaInfo, is_project_component) TEST_F(NodeMetaInfo, is_not_project_component) { using QmlDesigner::Storage::TypeTraits; - auto moduleId = projectStorageMock.createModule("/path/to/project"); + auto moduleId = projectStorageMock.createModule("/path/to/project", ModuleKind::PathLibrary); auto typeId = projectStorageMock.createType(moduleId, "Foo", {}); QmlDesigner::NodeMetaInfo metaInfo{typeId, &projectStorageMock}; @@ -262,7 +266,7 @@ TEST_F(NodeMetaInfo, invalid_is_not_project_component) TEST_F(NodeMetaInfo, is_in_project_module) { - auto moduleId = projectStorageMock.createModule("/path/to/project"); + auto moduleId = projectStorageMock.createModule("/path/to/project", ModuleKind::PathLibrary); TypeTraits traits{TypeTraitsKind::Reference}; traits.isInProjectModule = true; auto typeId = projectStorageMock.createType(moduleId, "Foo", traits); @@ -276,7 +280,7 @@ TEST_F(NodeMetaInfo, is_in_project_module) TEST_F(NodeMetaInfo, is_not_in_project_module) { using QmlDesigner::Storage::TypeTraits; - auto moduleId = projectStorageMock.createModule("/path/to/project"); + auto moduleId = projectStorageMock.createModule("/path/to/project", ModuleKind::PathLibrary); auto typeId = projectStorageMock.createType(moduleId, "Foo", {}); QmlDesigner::NodeMetaInfo metaInfo{typeId, &projectStorageMock}; @@ -873,7 +877,7 @@ TEST_F(NodeMetaInfo, second_input_is_invalid_for_common_base_returns_invalid) TEST_F(NodeMetaInfo, source_id) { - auto moduleId = projectStorageMock.createModule("/path/to/project"); + auto moduleId = projectStorageMock.createModule("/path/to/project", ModuleKind::PathLibrary); auto typeSourceId = QmlDesigner::SourceId::create(999); auto typeId = projectStorageMock.createType(moduleId, "Foo", {}, {}, typeSourceId); QmlDesigner::NodeMetaInfo metaInfo{typeId, &projectStorageMock}; @@ -930,7 +934,7 @@ TEST_F(NodeMetaInfo, default_is_not_color) TEST_F(NodeMetaInfo, float_is_a_floating_type) { - auto metaInfo = createMetaInfo("QML-cppnative", "float"); + auto metaInfo = createMetaInfo("QML", ModuleKind::CppLibrary, "float"); bool isType = metaInfo.isFloat(); @@ -957,7 +961,7 @@ TEST_F(NodeMetaInfo, default_is_not_float) TEST_F(NodeMetaInfo, is_FlowView_FlowActionArea) { - auto metaInfo = createDerivedDummyMetaInfo("FlowView", "FlowActionArea"); + auto metaInfo = createDerivedDummyMetaInfo("FlowView", ModuleKind::QmlLibrary, "FlowActionArea"); bool isType = metaInfo.isFlowViewFlowActionArea(); @@ -975,7 +979,7 @@ TEST_F(NodeMetaInfo, default_is_not_FlowView_FlowActionArea) TEST_F(NodeMetaInfo, is_FlowView_FlowDecision) { - auto metaInfo = createDerivedDummyMetaInfo("FlowView", "FlowDecision"); + auto metaInfo = createDerivedDummyMetaInfo("FlowView", ModuleKind::QmlLibrary, "FlowDecision"); bool isType = metaInfo.isFlowViewFlowDecision(); @@ -993,7 +997,7 @@ TEST_F(NodeMetaInfo, default_is_not_FlowView_FlowDecision) TEST_F(NodeMetaInfo, is_FlowView_FlowItem) { - auto metaInfo = createDerivedDummyMetaInfo("FlowView", "FlowItem"); + auto metaInfo = createDerivedDummyMetaInfo("FlowView", ModuleKind::QmlLibrary, "FlowItem"); bool isType = metaInfo.isFlowViewFlowItem(); @@ -1011,7 +1015,7 @@ TEST_F(NodeMetaInfo, default_is_not_FlowView_FlowItem) TEST_F(NodeMetaInfo, is_FlowView_FlowTransition) { - auto metaInfo = createDerivedDummyMetaInfo("FlowView", "FlowTransition"); + auto metaInfo = createDerivedDummyMetaInfo("FlowView", ModuleKind::QmlLibrary, "FlowTransition"); bool isType = metaInfo.isFlowViewFlowTransition(); @@ -1029,7 +1033,7 @@ TEST_F(NodeMetaInfo, default_is_not_FlowView_FlowTransition) TEST_F(NodeMetaInfo, is_FlowView_FlowView) { - auto metaInfo = createDerivedDummyMetaInfo("FlowView", "FlowView"); + auto metaInfo = createDerivedDummyMetaInfo("FlowView", ModuleKind::QmlLibrary, "FlowView"); bool isType = metaInfo.isFlowViewFlowView(); @@ -1047,7 +1051,7 @@ TEST_F(NodeMetaInfo, default_is_not_FlowView_FlowView) TEST_F(NodeMetaInfo, is_FlowView_FlowWildcard) { - auto metaInfo = createDerivedDummyMetaInfo("FlowView", "FlowWildcard"); + auto metaInfo = createDerivedDummyMetaInfo("FlowView", ModuleKind::QmlLibrary, "FlowWildcard"); bool isType = metaInfo.isFlowViewFlowWildcard(); @@ -1065,7 +1069,7 @@ TEST_F(NodeMetaInfo, default_is_not_FlowView_FlowWildcard) TEST_F(NodeMetaInfo, FlowItem_is_FlowView_item) { - auto metaInfo = createDerivedDummyMetaInfo("FlowView", "FlowItem"); + auto metaInfo = createDerivedDummyMetaInfo("FlowView", ModuleKind::QmlLibrary, "FlowItem"); bool isType = metaInfo.isFlowViewItem(); @@ -1074,7 +1078,7 @@ TEST_F(NodeMetaInfo, FlowItem_is_FlowView_item) TEST_F(NodeMetaInfo, FlowWildcard_is_FlowView_item) { - auto metaInfo = createDerivedDummyMetaInfo("FlowView", "FlowWildcard"); + auto metaInfo = createDerivedDummyMetaInfo("FlowView", ModuleKind::QmlLibrary, "FlowWildcard"); bool isType = metaInfo.isFlowViewItem(); @@ -1083,7 +1087,7 @@ TEST_F(NodeMetaInfo, FlowWildcard_is_FlowView_item) TEST_F(NodeMetaInfo, FlowDecision_is_FlowView_item) { - auto metaInfo = createDerivedDummyMetaInfo("FlowView", "FlowDecision"); + auto metaInfo = createDerivedDummyMetaInfo("FlowView", ModuleKind::QmlLibrary, "FlowDecision"); bool isType = metaInfo.isFlowViewItem(); @@ -1128,7 +1132,7 @@ TEST_F(NodeMetaInfo, QtQuick_Item_is_graphical_item) TEST_F(NodeMetaInfo, QtQuickWindow_Window_is_graphical_item) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Window", "Window"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Window", ModuleKind::QmlLibrary, "Window"); bool isType = metaInfo.isGraphicalItem(); @@ -1137,7 +1141,7 @@ TEST_F(NodeMetaInfo, QtQuickWindow_Window_is_graphical_item) TEST_F(NodeMetaInfo, QtQuickDialogs_Dialogs_is_graphical_item) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Dialogs", "Dialog"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Dialogs", ModuleKind::QmlLibrary, "Dialog"); bool isType = metaInfo.isGraphicalItem(); @@ -1146,7 +1150,7 @@ TEST_F(NodeMetaInfo, QtQuickDialogs_Dialogs_is_graphical_item) TEST_F(NodeMetaInfo, QtQuickControls_Popup_is_graphical_item) { - auto metaInfo = createMetaInfo("QtQuick.Controls", "Popup"); + auto metaInfo = createMetaInfo("QtQuick.Controls", ModuleKind::QmlLibrary, "Popup"); bool isType = metaInfo.isGraphicalItem(); @@ -1191,7 +1195,7 @@ TEST_F(NodeMetaInfo, QtQuick_Positioner_is_layoutable) TEST_F(NodeMetaInfo, QtQuick_Layouts_Layout_is_layoutable) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Layouts", "Layout"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Layouts", ModuleKind::QmlLibrary, "Layout"); bool isType = metaInfo.isLayoutable(); @@ -1200,7 +1204,7 @@ TEST_F(NodeMetaInfo, QtQuick_Layouts_Layout_is_layoutable) TEST_F(NodeMetaInfo, QtQuick_Controls_SplitView_is_layoutable) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Controls", "SplitView"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Controls", ModuleKind::QmlLibrary, "SplitView"); bool isType = metaInfo.isLayoutable(); @@ -1263,7 +1267,8 @@ TEST_F(NodeMetaInfo, default_is_not_qml_component) TEST_F(NodeMetaInfo, is_QtMultimedia_SoundEffect) { - auto qtMultimediaModuleId = projectStorageMock.createModule("QtMultimedia"); + auto qtMultimediaModuleId = projectStorageMock.createModule("QtMultimedia", + ModuleKind::QmlLibrary); auto metaInfo = createDerivedDummyMetaInfo(qtMultimediaModuleId, "SoundEffect"); bool isType = metaInfo.isQtMultimediaSoundEffect(); @@ -1300,7 +1305,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtObject) TEST_F(NodeMetaInfo, is_QtQuick3D_BakedLightmap) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", "BakedLightmap"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", ModuleKind::QmlLibrary, "BakedLightmap"); bool isType = metaInfo.isQtQuick3DBakedLightmap(); @@ -1318,7 +1323,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick3D_BakedLightmap) TEST_F(NodeMetaInfo, is_QtQuick3D_Camera) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", "Camera"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", ModuleKind::QmlLibrary, "Camera"); bool isType = metaInfo.isQtQuick3DCamera(); @@ -1336,7 +1341,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick3D_Camera) TEST_F(NodeMetaInfo, is_QtQuick3D_Command) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", "Command"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", ModuleKind::QmlLibrary, "Command"); bool isType = metaInfo.isQtQuick3DCommand(); @@ -1354,7 +1359,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick3D_Command) TEST_F(NodeMetaInfo, is_QtQuick3D_DefaultMaterial) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", "DefaultMaterial"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", ModuleKind::QmlLibrary, "DefaultMaterial"); bool isType = metaInfo.isQtQuick3DDefaultMaterial(); @@ -1372,7 +1377,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick3D_DefaultMaterial) TEST_F(NodeMetaInfo, is_QtQuick3D_Effect) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", "Effect"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", ModuleKind::QmlLibrary, "Effect"); bool isType = metaInfo.isQtQuick3DEffect(); @@ -1390,7 +1395,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick3D_Effect) TEST_F(NodeMetaInfo, is_QtQuick3D_InstanceList) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", "InstanceList"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", ModuleKind::QmlLibrary, "InstanceList"); bool isType = metaInfo.isQtQuick3DInstanceList(); @@ -1408,7 +1413,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick3D_InstanceList) TEST_F(NodeMetaInfo, is_QtQuick3D_InstanceListEntry) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", "InstanceListEntry"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", ModuleKind::QmlLibrary, "InstanceListEntry"); bool isType = metaInfo.isQtQuick3DInstanceListEntry(); @@ -1426,7 +1431,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick3D_InstanceListEntry) TEST_F(NodeMetaInfo, is_QtQuick3D_Light) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", "Light"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", ModuleKind::QmlLibrary, "Light"); bool isType = metaInfo.isQtQuick3DLight(); @@ -1444,7 +1449,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick3D_Light) TEST_F(NodeMetaInfo, is_QtQuick3D_Material) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", "Material"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", ModuleKind::QmlLibrary, "Material"); bool isType = metaInfo.isQtQuick3DMaterial(); @@ -1462,7 +1467,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick3D_Material) TEST_F(NodeMetaInfo, is_QtQuick3D_Model) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", "Model"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", ModuleKind::QmlLibrary, "Model"); bool isType = metaInfo.isQtQuick3DModel(); @@ -1480,7 +1485,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick3D_Model) TEST_F(NodeMetaInfo, is_QtQuick3D_Node) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", "Node"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", ModuleKind::QmlLibrary, "Node"); bool isType = metaInfo.isQtQuick3DNode(); @@ -1498,7 +1503,8 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick3D_Node) TEST_F(NodeMetaInfo, is_QtQuick3D_Particles3D_cppnative_QQuick3DParticleAbstractShape) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D.Particles3D-cppnative", + auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D.Particles3D", + ModuleKind::CppLibrary, "QQuick3DParticleAbstractShape"); bool isType = metaInfo.isQtQuick3DParticlesAbstractShape(); @@ -1517,7 +1523,9 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick3D_Particles3D_cppnative_QQuick3DPart TEST_F(NodeMetaInfo, is_QtQuick3D_Particles3D_Affector3D) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D.Particles3D", "Affector3D"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D.Particles3D", + ModuleKind::QmlLibrary, + "Affector3D"); bool isType = metaInfo.isQtQuick3DParticles3DAffector3D(); @@ -1535,7 +1543,9 @@ TEST_F(NodeMetaInfo, QtQuick3D_Particles3D_Affector3D) TEST_F(NodeMetaInfo, is_QtQuick3D_Particles3D_Attractor3D) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D.Particles3D", "Attractor3D"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D.Particles3D", + ModuleKind::QmlLibrary, + "Attractor3D"); bool isType = metaInfo.isQtQuick3DParticles3DAttractor3D(); @@ -1553,7 +1563,9 @@ TEST_F(NodeMetaInfo, QtQuick3D_Particles3D_Attractor3D) TEST_F(NodeMetaInfo, is_QtQuick3D_Particles3D_Particle3D) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D.Particles3D", "Particle3D"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D.Particles3D", + ModuleKind::QmlLibrary, + "Particle3D"); bool isType = metaInfo.isQtQuick3DParticles3DParticle3D(); @@ -1571,7 +1583,9 @@ TEST_F(NodeMetaInfo, QtQuick3D_Particles3D_Particle3D) TEST_F(NodeMetaInfo, is_QtQuick3D_Particles3D_ParticleEmitter3D) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D.Particles3D", "ParticleEmitter3D"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D.Particles3D", + ModuleKind::QmlLibrary, + "ParticleEmitter3D"); bool isType = metaInfo.isQtQuick3DParticles3DParticleEmitter3D(); @@ -1589,7 +1603,9 @@ TEST_F(NodeMetaInfo, QtQuick3D_Particles3D_ParticleEmitter3D) TEST_F(NodeMetaInfo, is_QtQuick3D_Particles3D_SpriteParticle3D) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D.Particles3D", "SpriteParticle3D"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D.Particles3D", + ModuleKind::QmlLibrary, + "SpriteParticle3D"); bool isType = metaInfo.isQtQuick3DParticles3DSpriteParticle3D(); @@ -1607,7 +1623,7 @@ TEST_F(NodeMetaInfo, QtQuick3D_Particles3D_SpriteParticle3D) TEST_F(NodeMetaInfo, is_QtQuick3D_Pass) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", "Pass"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", ModuleKind::QmlLibrary, "Pass"); bool isType = metaInfo.isQtQuick3DPass(); @@ -1625,7 +1641,9 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick3D_Pass) TEST_F(NodeMetaInfo, is_QtQuick3D_PrincipledMaterial) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", "PrincipledMaterial"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", + ModuleKind::QmlLibrary, + "PrincipledMaterial"); bool isType = metaInfo.isQtQuick3DPrincipledMaterial(); @@ -1643,7 +1661,9 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick3D_PrincipledMaterial) TEST_F(NodeMetaInfo, is_QtQuick3D_SpecularGlossyMaterial) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", "SpecularGlossyMaterial"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", + ModuleKind::QmlLibrary, + "SpecularGlossyMaterial"); bool isType = metaInfo.isQtQuick3DSpecularGlossyMaterial(); @@ -1661,7 +1681,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick3D_SpecularGlossyMaterial) TEST_F(NodeMetaInfo, is_QtQuick3D_SceneEnvironment) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", "SceneEnvironment"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", ModuleKind::QmlLibrary, "SceneEnvironment"); bool isType = metaInfo.isQtQuick3DSceneEnvironment(); @@ -1679,7 +1699,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick3D_SceneEnvironment) TEST_F(NodeMetaInfo, is_QtQuick3D_Shader) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", "Shader"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", ModuleKind::QmlLibrary, "Shader"); bool isType = metaInfo.isQtQuick3DShader(); @@ -1697,7 +1717,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick3D_Shader) TEST_F(NodeMetaInfo, is_QtQuick3D_Texture) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", "Texture"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", ModuleKind::QmlLibrary, "Texture"); bool isType = metaInfo.isQtQuick3DTexture(); @@ -1715,7 +1735,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick3D_Texture) TEST_F(NodeMetaInfo, is_QtQuick3D_TextureInput) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", "TextureInput"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", ModuleKind::QmlLibrary, "TextureInput"); bool isType = metaInfo.isQtQuick3DTextureInput(); @@ -1733,7 +1753,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick3D_TextureInput) TEST_F(NodeMetaInfo, is_QtQuick3D_CubeMapTexture) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", "CubeMapTexture"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", ModuleKind::QmlLibrary, "CubeMapTexture"); bool isType = metaInfo.isQtQuick3DCubeMapTexture(); @@ -1751,7 +1771,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick3D_CubeMapTexture) TEST_F(NodeMetaInfo, is_QtQuick3D_View3D) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", "View3D"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick3D", ModuleKind::QmlLibrary, "View3D"); bool isType = metaInfo.isQtQuick3DView3D(); @@ -1769,7 +1789,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick3D_View3D) TEST_F(NodeMetaInfo, is_QtQuick_BorderImage) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick", "BorderImage"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick", ModuleKind::QmlLibrary, "BorderImage"); bool isType = metaInfo.isQtQuickBorderImage(); @@ -1787,7 +1807,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick_BorderImage) TEST_F(NodeMetaInfo, is_QtQuickControls_SwipeView) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Controls", "SwipeView"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Controls", ModuleKind::QmlLibrary, "SwipeView"); bool isType = metaInfo.isQtQuickControlsSwipeView(); @@ -1805,7 +1825,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuickControls_SwipeView) TEST_F(NodeMetaInfo, is_QtQuickControls_TabBar) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Controls", "TabBar"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Controls", ModuleKind::QmlLibrary, "TabBar"); bool isType = metaInfo.isQtQuickControlsTabBar(); @@ -1823,7 +1843,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuickControls_TabBar) TEST_F(NodeMetaInfo, is_QtQuickExtras_Picture) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Extras", "Picture"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Extras", ModuleKind::QmlLibrary, "Picture"); bool isType = metaInfo.isQtQuickExtrasPicture(); @@ -1841,7 +1861,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuickExtras_Picture) TEST_F(NodeMetaInfo, is_QtQuick_Image) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick", "Image"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick", ModuleKind::QmlLibrary, "Image"); bool isType = metaInfo.isQtQuickImage(); @@ -1859,7 +1879,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick_Image) TEST_F(NodeMetaInfo, is_QtQuick_Item) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick", "Item"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick", ModuleKind::QmlLibrary, "Item"); bool isType = metaInfo.isQtQuickItem(); @@ -1877,7 +1897,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick_Item) TEST_F(NodeMetaInfo, is_QtQuickLayouts_BorderImage) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Layouts", "Layout"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Layouts", ModuleKind::QmlLibrary, "Layout"); bool isType = metaInfo.isQtQuickLayoutsLayout(); @@ -1895,7 +1915,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuickLayouts_Layout) TEST_F(NodeMetaInfo, is_QtQuick_Loader) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick", "Loader"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick", ModuleKind::QmlLibrary, "Loader"); bool isType = metaInfo.isQtQuickLoader(); @@ -1913,7 +1933,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick_Loader) TEST_F(NodeMetaInfo, is_QtQuick_Path) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick", "Path"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick", ModuleKind::QmlLibrary, "Path"); bool isType = metaInfo.isQtQuickPath(); @@ -1931,7 +1951,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick_Path) TEST_F(NodeMetaInfo, is_QtQuick_PauseAnimation) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick", "PauseAnimation"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick", ModuleKind::QmlLibrary, "PauseAnimation"); bool isType = metaInfo.isQtQuickPauseAnimation(); @@ -1949,7 +1969,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick_PauseAnimation) TEST_F(NodeMetaInfo, is_QtQuick_Positioner) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick", "Positioner"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick", ModuleKind::QmlLibrary, "Positioner"); bool isType = metaInfo.isQtQuickPositioner(); @@ -1967,7 +1987,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick_Positioner) TEST_F(NodeMetaInfo, is_QtQuick_PropertyAnimation) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick", "PropertyAnimation"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick", ModuleKind::QmlLibrary, "PropertyAnimation"); bool isType = metaInfo.isQtQuickPropertyAnimation(); @@ -1985,7 +2005,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick_PropertyAnimation) TEST_F(NodeMetaInfo, is_QtQuick_PropertyChanges) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick", "PropertyChanges"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick", ModuleKind::QmlLibrary, "PropertyChanges"); bool isType = metaInfo.isQtQuickPropertyChanges(); @@ -2003,7 +2023,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick_PropertyChanges) TEST_F(NodeMetaInfo, is_QtQuick_Repeater) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick", "Repeater"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick", ModuleKind::QmlLibrary, "Repeater"); bool isType = metaInfo.isQtQuickRepeater(); @@ -2021,7 +2041,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick_Repeater) TEST_F(NodeMetaInfo, is_QtQuick_State) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick", "State"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick", ModuleKind::QmlLibrary, "State"); bool isType = metaInfo.isQtQuickState(); @@ -2039,7 +2059,9 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick_State) TEST_F(NodeMetaInfo, is_QtQuickNative_StateOperation) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick-cppnative", "QQuickStateOperation"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick", + ModuleKind::CppLibrary, + "QQuickStateOperation"); bool isType = metaInfo.isQtQuickStateOperation(); @@ -2057,7 +2079,9 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuickNative_StateOperation) TEST_F(NodeMetaInfo, is_QtQuickStudioComponents_GroupItem) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Studio.Components", "GroupItem"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Studio.Components", + ModuleKind::QmlLibrary, + "GroupItem"); bool isType = metaInfo.isQtQuickStudioComponentsGroupItem(); @@ -2075,7 +2099,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuickStudioComponents_GroupItem) TEST_F(NodeMetaInfo, is_QtQuick_Text) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick", "Text"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick", ModuleKind::QmlLibrary, "Text"); bool isType = metaInfo.isQtQuickText(); @@ -2093,7 +2117,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick_Text) TEST_F(NodeMetaInfo, is_QtQuickTimeline_Keyframe) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Timeline", "Keyframe"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Timeline", ModuleKind::QmlLibrary, "Keyframe"); bool isType = metaInfo.isQtQuickTimelineKeyframe(); @@ -2111,7 +2135,9 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick_Keyframe) TEST_F(NodeMetaInfo, is_QtQuickTimeline_KeyframeGroup) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Timeline", "KeyframeGroup"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Timeline", + ModuleKind::QmlLibrary, + "KeyframeGroup"); bool isType = metaInfo.isQtQuickTimelineKeyframeGroup(); @@ -2129,7 +2155,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick_KeyframeGroup) TEST_F(NodeMetaInfo, is_QtQuickTimeline_Timeline) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Timeline", "Timeline"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Timeline", ModuleKind::QmlLibrary, "Timeline"); bool isType = metaInfo.isQtQuickTimelineTimeline(); @@ -2147,7 +2173,9 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick_Timeline) TEST_F(NodeMetaInfo, is_QtQuickTimeline_TimelineAnimation) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Timeline", "TimelineAnimation"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Timeline", + ModuleKind::QmlLibrary, + "TimelineAnimation"); bool isType = metaInfo.isQtQuickTimelineTimelineAnimation(); @@ -2165,7 +2193,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick_TimelineAnimation) TEST_F(NodeMetaInfo, is_QtQuick_Transition) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick", "Transition"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick", ModuleKind::QmlLibrary, "Transition"); bool isType = metaInfo.isQtQuickTransition(); @@ -2183,7 +2211,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuick_Transition) TEST_F(NodeMetaInfo, is_QtQuickWindow_Window) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Window", "Window"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Window", ModuleKind::QmlLibrary, "Window"); bool isType = metaInfo.isQtQuickWindowWindow(); @@ -2201,7 +2229,9 @@ TEST_F(NodeMetaInfo, default_is_not_QtQuickWindow_Window) TEST_F(NodeMetaInfo, is_QtSafeRenderer_SafeRendererPicture) { - auto metaInfo = createDerivedDummyMetaInfo("Qt.SafeRenderer", "SafeRendererPicture"); + auto metaInfo = createDerivedDummyMetaInfo("Qt.SafeRenderer", + ModuleKind::QmlLibrary, + "SafeRendererPicture"); bool isType = metaInfo.isQtSafeRendererSafeRendererPicture(); @@ -2219,7 +2249,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtSafeRenderer_SafeRendererPicture) TEST_F(NodeMetaInfo, is_QtSafeRenderer_SafePicture) { - auto metaInfo = createDerivedDummyMetaInfo("Qt.SafeRenderer", "SafePicture"); + auto metaInfo = createDerivedDummyMetaInfo("Qt.SafeRenderer", ModuleKind::QmlLibrary, "SafePicture"); bool isType = metaInfo.isQtSafeRendererSafePicture(); @@ -2237,7 +2267,7 @@ TEST_F(NodeMetaInfo, default_is_not_QtSafeRenderer_SafePicture) TEST_F(NodeMetaInfo, is_string) { - auto metaInfo = createMetaInfo("QML", "string"); + auto metaInfo = createMetaInfo("QML", ModuleKind::QmlLibrary, "string"); bool isType = metaInfo.isString(); @@ -2255,7 +2285,7 @@ TEST_F(NodeMetaInfo, default_is_not_string) TEST_F(NodeMetaInfo, QtQuick_Item_is_suitable_for_MouseArea_fill) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick", "Item"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick", ModuleKind::QmlLibrary, "Item"); bool isType = metaInfo.isSuitableForMouseAreaFill(); @@ -2264,7 +2294,7 @@ TEST_F(NodeMetaInfo, QtQuick_Item_is_suitable_for_MouseArea_fill) TEST_F(NodeMetaInfo, QtQuick_MouseArea_is_suitable_for_MouseArea_fill) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick", "MouseArea"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick", ModuleKind::QmlLibrary, "MouseArea"); bool isType = metaInfo.isSuitableForMouseAreaFill(); @@ -2273,7 +2303,7 @@ TEST_F(NodeMetaInfo, QtQuick_MouseArea_is_suitable_for_MouseArea_fill) TEST_F(NodeMetaInfo, QtQuickControls_Control_is_suitable_for_MouseArea_fill) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Controls", "Control"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Controls", ModuleKind::QmlLibrary, "Control"); bool isType = metaInfo.isSuitableForMouseAreaFill(); @@ -2282,7 +2312,7 @@ TEST_F(NodeMetaInfo, QtQuickControls_Control_is_suitable_for_MouseArea_fill) TEST_F(NodeMetaInfo, QtQuickTemplates_Control_is_suitable_for_MouseArea_fill) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Templates", "Control"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick.Templates", ModuleKind::QmlLibrary, "Control"); bool isType = metaInfo.isSuitableForMouseAreaFill(); @@ -2300,7 +2330,7 @@ TEST_F(NodeMetaInfo, default_is_not_suitable_for_MouseArea_fill) TEST_F(NodeMetaInfo, is_url) { - auto metaInfo = createMetaInfo("QML", "url"); + auto metaInfo = createMetaInfo("QML", ModuleKind::QmlLibrary, "url"); bool isType = metaInfo.isUrl(); @@ -2318,7 +2348,7 @@ TEST_F(NodeMetaInfo, default_is_not_url) TEST_F(NodeMetaInfo, is_variant) { - auto metaInfo = createMetaInfo("QML", "var"); + auto metaInfo = createMetaInfo("QML", ModuleKind::QmlLibrary, "var"); bool isType = metaInfo.isVariant(); @@ -2336,7 +2366,7 @@ TEST_F(NodeMetaInfo, default_is_not_variant) TEST_F(NodeMetaInfo, is_vector2d) { - auto metaInfo = createMetaInfo("QtQuick", "vector2d"); + auto metaInfo = createMetaInfo("QtQuick", ModuleKind::QmlLibrary, "vector2d"); bool isType = metaInfo.isVector2D(); @@ -2354,7 +2384,7 @@ TEST_F(NodeMetaInfo, default_is_not_vector2d) TEST_F(NodeMetaInfo, is_vector3d) { - auto metaInfo = createMetaInfo("QtQuick", "vector3d"); + auto metaInfo = createMetaInfo("QtQuick", ModuleKind::QmlLibrary, "vector3d"); bool isType = metaInfo.isVector3D(); @@ -2372,7 +2402,7 @@ TEST_F(NodeMetaInfo, default_is_not_vector3d) TEST_F(NodeMetaInfo, is_vector4d) { - auto metaInfo = createMetaInfo("QtQuick", "vector4d"); + auto metaInfo = createMetaInfo("QtQuick", ModuleKind::QmlLibrary, "vector4d"); bool isType = metaInfo.isVector4D(); @@ -2390,7 +2420,7 @@ TEST_F(NodeMetaInfo, default_is_not_vector4d) TEST_F(NodeMetaInfo, QtQuick_ListView_is_view) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick", "ListView"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick", ModuleKind::QmlLibrary, "ListView"); bool isType = metaInfo.isView(); @@ -2399,7 +2429,7 @@ TEST_F(NodeMetaInfo, QtQuick_ListView_is_view) TEST_F(NodeMetaInfo, QtQuick_GridView_is_view) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick", "GridView"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick", ModuleKind::QmlLibrary, "GridView"); bool isType = metaInfo.isView(); @@ -2408,7 +2438,7 @@ TEST_F(NodeMetaInfo, QtQuick_GridView_is_view) TEST_F(NodeMetaInfo, QtQuick_PathView_is_view) { - auto metaInfo = createDerivedDummyMetaInfo("QtQuick", "PathView"); + auto metaInfo = createDerivedDummyMetaInfo("QtQuick", ModuleKind::QmlLibrary, "PathView"); bool isType = metaInfo.isView(); @@ -2428,7 +2458,7 @@ TEST_F(NodeMetaInfo, is_enumeration) { TypeTraits traits; traits.isEnum = true; - auto metaInfo = createMetaInfo("QML", "Foo", traits); + auto metaInfo = createMetaInfo("QML", ModuleKind::QmlLibrary, "Foo", traits); bool isType = metaInfo.isEnumeration(); @@ -2437,7 +2467,7 @@ TEST_F(NodeMetaInfo, is_enumeration) TEST_F(NodeMetaInfo, is_not_enumeration) { - auto metaInfo = createMetaInfo("QML", "Foo", {}); + auto metaInfo = createMetaInfo("QML", ModuleKind::QmlLibrary, "Foo", {}); bool isType = metaInfo.isEnumeration(); @@ -2457,7 +2487,7 @@ TEST_F(NodeMetaInfo, all_external_type_names) { QmlDesigner::Storage::Info::ExportedTypeNames names{{qmlModuleId, "Object", 2, -1}, {qmlModuleId, "Obj", 2, 1}}; - auto metaInfo = createMetaInfo("QML", "Foo"); + auto metaInfo = createMetaInfo("QML", ModuleKind::QmlLibrary, "Foo"); ON_CALL(projectStorageMock, exportedTypeNames(metaInfo.id())).WillByDefault(Return(names)); auto exportedTypeNames = metaInfo.allExportedTypeNames(); @@ -2483,7 +2513,7 @@ TEST_F(NodeMetaInfo, external_type_names_for_source_id) { QmlDesigner::Storage::Info::ExportedTypeNames names{{qmlModuleId, "Object", 2, -1}, {qmlModuleId, "Obj", 2, 1}}; - auto metaInfo = createMetaInfo("QML", "Foo"); + auto metaInfo = createMetaInfo("QML", ModuleKind::QmlLibrary, "Foo"); ON_CALL(projectStorageMock, exportedTypeNames(metaInfo.id(), model.fileUrlSourceId())) .WillByDefault(Return(names)); @@ -2511,7 +2541,7 @@ TEST_F(NodeMetaInfo, invalid_source_id_has_no_external_type_names_for_source_id) { QmlDesigner::Storage::Info::ExportedTypeNames names{{qmlModuleId, "Object", 2, -1}, {qmlModuleId, "Obj", 2, 1}}; - auto metaInfo = createMetaInfo("QML", "Foo"); + auto metaInfo = createMetaInfo("QML", ModuleKind::QmlLibrary, "Foo"); ON_CALL(projectStorageMock, exportedTypeNames(metaInfo.id(), model.fileUrlSourceId())) .WillByDefault(Return(names)); QmlDesigner::SourceId sourceId; @@ -2523,7 +2553,7 @@ TEST_F(NodeMetaInfo, invalid_source_id_has_no_external_type_names_for_source_id) TEST_F(NodeMetaInfo, float_is_a_number) { - auto metaInfo = createMetaInfo("QML-cppnative", "float"); + auto metaInfo = createMetaInfo("QML", ModuleKind::CppLibrary, "float"); bool isType = metaInfo.isNumber(); @@ -2550,7 +2580,7 @@ TEST_F(NodeMetaInfo, int_is_a_number) TEST_F(NodeMetaInfo, uint_is_a_number) { - auto metaInfo = createMetaInfo("QML-cppnative", "uint"); + auto metaInfo = createMetaInfo("QML", ModuleKind::CppLibrary, "uint"); bool isType = metaInfo.isNumber(); @@ -2568,7 +2598,7 @@ TEST_F(NodeMetaInfo, default_is_not_number) TEST_F(NodeMetaInfo, property_editor_specifics_path) { - auto metaInfo = createMetaInfo("QtQuick", "Item"); + auto metaInfo = createMetaInfo("QtQuick", ModuleKind::QmlLibrary, "Item"); auto pathId = QmlDesigner::SourceId::create(45); ON_CALL(projectStorageMock, propertyEditorPathId(metaInfo.id())).WillByDefault(Return(pathId)); @@ -2588,7 +2618,7 @@ TEST_F(NodeMetaInfo, default_property_editor_specifics_path_is_empty) TEST_F(NodeMetaInfo, is_reference) { - auto metaInfo = createMetaInfo("QtQuick", "Item", TypeTraitsKind::Reference); + auto metaInfo = createMetaInfo("QtQuick", ModuleKind::QmlLibrary, "Item", TypeTraitsKind::Reference); auto type = metaInfo.type(); @@ -2597,7 +2627,7 @@ TEST_F(NodeMetaInfo, is_reference) TEST_F(NodeMetaInfo, is_value) { - auto metaInfo = createMetaInfo("QML", "bool", TypeTraitsKind::Value); + auto metaInfo = createMetaInfo("QML", ModuleKind::QmlLibrary, "bool", TypeTraitsKind::Value); auto type = metaInfo.type(); @@ -2606,7 +2636,7 @@ TEST_F(NodeMetaInfo, is_value) TEST_F(NodeMetaInfo, is_sequence) { - auto metaInfo = createMetaInfo("QML", "QObjectList", TypeTraitsKind::Sequence); + auto metaInfo = createMetaInfo("QML", ModuleKind::QmlLibrary, "QObjectList", TypeTraitsKind::Sequence); auto type = metaInfo.type(); @@ -2615,7 +2645,7 @@ TEST_F(NodeMetaInfo, is_sequence) TEST_F(NodeMetaInfo, is_none) { - auto metaInfo = createMetaInfo("QML", "void", TypeTraitsKind::None); + auto metaInfo = createMetaInfo("QML", ModuleKind::QmlLibrary, "void", TypeTraitsKind::None); auto type = metaInfo.type(); @@ -2657,7 +2687,7 @@ TEST_F(NodeMetaInfo, invalid_can_not_be_container) TEST_F(NodeMetaInfo, component_can_be_container) { - auto moduleId = projectStorageMock.createModule("/path/to/project"); + auto moduleId = projectStorageMock.createModule("/path/to/project", ModuleKind::PathLibrary); TypeTraits traits{TypeTraitsKind::Reference}; traits.canBeContainer = FlagIs::True; auto typeId = projectStorageMock.createType(moduleId, "Foo", traits); @@ -2694,7 +2724,7 @@ TEST_F(NodeMetaInfo, invalid_do_no_forces_clipping) TEST_F(NodeMetaInfo, component_forces_clipping) { - auto moduleId = projectStorageMock.createModule("/path/to/project"); + auto moduleId = projectStorageMock.createModule("/path/to/project", ModuleKind::PathLibrary); TypeTraits traits{TypeTraitsKind::Reference}; traits.forceClip = FlagIs::True; auto typeId = projectStorageMock.createType(moduleId, "Foo", traits); @@ -2731,7 +2761,7 @@ TEST_F(NodeMetaInfo, invalid_does_not_layout_children) TEST_F(NodeMetaInfo, component_layouts_children) { - auto moduleId = projectStorageMock.createModule("/path/to/project"); + auto moduleId = projectStorageMock.createModule("/path/to/project", ModuleKind::PathLibrary); TypeTraits traits{TypeTraitsKind::Reference}; traits.doesLayoutChildren = FlagIs::True; auto typeId = projectStorageMock.createType(moduleId, "Foo", traits); @@ -2768,7 +2798,7 @@ TEST_F(NodeMetaInfo, invalid_cannot_be_dropped_in_form_editor) TEST_F(NodeMetaInfo, component_can_be_dropped_in_form_editor) { - auto moduleId = projectStorageMock.createModule("/path/to/project"); + auto moduleId = projectStorageMock.createModule("/path/to/project", ModuleKind::PathLibrary); TypeTraits traits{TypeTraitsKind::Reference}; traits.canBeDroppedInFormEditor = FlagIs::True; auto typeId = projectStorageMock.createType(moduleId, "Foo", traits); @@ -2805,7 +2835,7 @@ TEST_F(NodeMetaInfo, invalid_cannot_be_dropped_in_navigator) TEST_F(NodeMetaInfo, component_can_be_dropped_in_navigator) { - auto moduleId = projectStorageMock.createModule("/path/to/project"); + auto moduleId = projectStorageMock.createModule("/path/to/project", ModuleKind::PathLibrary); TypeTraits traits{TypeTraitsKind::Reference}; traits.canBeDroppedInNavigator = FlagIs::True; auto typeId = projectStorageMock.createType(moduleId, "Foo", traits); @@ -2842,7 +2872,7 @@ TEST_F(NodeMetaInfo, invalid_cannot_be_dropped_in_3d_view) TEST_F(NodeMetaInfo, component_can_be_dropped_in_3d_view) { - auto moduleId = projectStorageMock.createModule("/path/to/project"); + auto moduleId = projectStorageMock.createModule("/path/to/project", ModuleKind::PathLibrary); TypeTraits traits{TypeTraitsKind::Reference}; traits.canBeDroppedInView3D = FlagIs::True; auto typeId = projectStorageMock.createType(moduleId, "Foo", traits); @@ -2879,7 +2909,7 @@ TEST_F(NodeMetaInfo, invalid_is_not_movable) TEST_F(NodeMetaInfo, component_is_movable) { - auto moduleId = projectStorageMock.createModule("/path/to/project"); + auto moduleId = projectStorageMock.createModule("/path/to/project", ModuleKind::PathLibrary); TypeTraits traits{TypeTraitsKind::Reference}; traits.isMovable = FlagIs::True; auto typeId = projectStorageMock.createType(moduleId, "Foo", traits); @@ -2916,7 +2946,7 @@ TEST_F(NodeMetaInfo, invalid_is_not_resizable) TEST_F(NodeMetaInfo, component_is_resizable) { - auto moduleId = projectStorageMock.createModule("/path/to/project"); + auto moduleId = projectStorageMock.createModule("/path/to/project", ModuleKind::PathLibrary); TypeTraits traits{TypeTraitsKind::Reference}; traits.isResizable = FlagIs::True; auto typeId = projectStorageMock.createType(moduleId, "Foo", traits); @@ -2953,7 +2983,7 @@ TEST_F(NodeMetaInfo, invalid_has_not_form_editor_item) TEST_F(NodeMetaInfo, component_has_form_editor_item) { - auto moduleId = projectStorageMock.createModule("/path/to/project"); + auto moduleId = projectStorageMock.createModule("/path/to/project", ModuleKind::PathLibrary); TypeTraits traits{TypeTraitsKind::Reference}; traits.hasFormEditorItem = FlagIs::True; auto typeId = projectStorageMock.createType(moduleId, "Foo", traits); @@ -2990,7 +3020,7 @@ TEST_F(NodeMetaInfo, invalid_is_not_stacked_container) TEST_F(NodeMetaInfo, component_is_stacked_container) { - auto moduleId = projectStorageMock.createModule("/path/to/project"); + auto moduleId = projectStorageMock.createModule("/path/to/project", ModuleKind::PathLibrary); TypeTraits traits{TypeTraitsKind::Reference}; traits.isStackedContainer = FlagIs::True; auto typeId = projectStorageMock.createType(moduleId, "Foo", traits); @@ -3027,7 +3057,7 @@ TEST_F(NodeMetaInfo, invalid_dont_takes_over_rendering_of_children) TEST_F(NodeMetaInfo, component_takes_over_rendering_of_children) { - auto moduleId = projectStorageMock.createModule("/path/to/project"); + auto moduleId = projectStorageMock.createModule("/path/to/project", ModuleKind::PathLibrary); TypeTraits traits{TypeTraitsKind::Reference}; traits.takesOverRenderingOfChildren = FlagIs::True; auto typeId = projectStorageMock.createType(moduleId, "Foo", traits); @@ -3064,7 +3094,7 @@ TEST_F(NodeMetaInfo, invalid_is_not_visible_in_navigator) TEST_F(NodeMetaInfo, component_is_visible_in_navigator) { - auto moduleId = projectStorageMock.createModule("/path/to/project"); + auto moduleId = projectStorageMock.createModule("/path/to/project", ModuleKind::PathLibrary); TypeTraits traits{TypeTraitsKind::Reference}; traits.visibleInNavigator = FlagIs::True; auto typeId = projectStorageMock.createType(moduleId, "Foo", traits); @@ -3101,7 +3131,7 @@ TEST_F(NodeMetaInfo, invalid_is_not_visible_in_library) TEST_F(NodeMetaInfo, component_is_visible_in_library) { - auto moduleId = projectStorageMock.createModule("/path/to/project"); + auto moduleId = projectStorageMock.createModule("/path/to/project", ModuleKind::PathLibrary); TypeTraits traits{TypeTraitsKind::Reference}; traits.visibleInLibrary = FlagIs::True; auto typeId = projectStorageMock.createType(moduleId, "Foo", traits); @@ -3152,6 +3182,7 @@ TEST_F(NodeMetaInfo, item_library_entries) { projectStorageMock.setItemLibraryEntries(objectMetaInfo.id(), {{objectMetaInfo.id(), + "QtObject", "Object", "/icon/path", "Basic", @@ -3163,6 +3194,7 @@ TEST_F(NodeMetaInfo, item_library_entries) ASSERT_THAT(entries, ElementsAre(IsItemLibraryEntry(objectMetaInfo.id(), + "QtObject", "Object", "/icon/path", "Basic", diff --git a/tests/unit/tests/unittests/metainfo/propertymetainfo-test.cpp b/tests/unit/tests/unittests/metainfo/propertymetainfo-test.cpp index 25436264aee..d2ec90b7a81 100644 --- a/tests/unit/tests/unittests/metainfo/propertymetainfo-test.cpp +++ b/tests/unit/tests/unittests/metainfo/propertymetainfo-test.cpp @@ -22,6 +22,7 @@ namespace { using QmlDesigner::Enumeration; using QmlDesigner::ModelNode; using QmlDesigner::ModelNodes; +using QmlDesigner::Storage::ModuleKind; using QmlDesigner::Storage::PropertyDeclarationTraits; using QmlDesigner::Storage::TypeTraits; @@ -29,10 +30,11 @@ class PropertyMetaInfo : public ::testing::Test { protected: QmlDesigner::NodeMetaInfo createNodeMetaInfo(Utils::SmallStringView moduleName, + ModuleKind moduleKind, Utils::SmallStringView typeName, QmlDesigner::Storage::TypeTraits typeTraits = {}) { - auto moduleId = projectStorageMock.createModule(moduleName); + auto moduleId = projectStorageMock.createModule(moduleName, moduleKind); auto typeId = projectStorageMock.createType(moduleId, typeName, typeTraits); return QmlDesigner::NodeMetaInfo{typeId, &projectStorageMock}; @@ -47,7 +49,7 @@ protected: QmlDesigner::Import::createLibraryImport("QtQuick"), QmlDesigner::Import::createLibraryImport("QtQml.Models")}, QUrl::fromLocalFile(pathCache.path.toQString())}; - QmlDesigner::NodeMetaInfo nodeInfo = createNodeMetaInfo("QtQuick", "Foo"); + QmlDesigner::NodeMetaInfo nodeInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "Foo"); }; TEST_F(PropertyMetaInfo, name) @@ -71,7 +73,7 @@ TEST_F(PropertyMetaInfo, default_has_no_name) TEST_F(PropertyMetaInfo, property_type) { - auto barInfo = createNodeMetaInfo("QtQuick", "Bar"); + auto barInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "Bar"); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, barInfo.id()); auto propertyInfo = nodeInfo.property("bar"); @@ -91,7 +93,7 @@ TEST_F(PropertyMetaInfo, default_hads_invalid_property_type) TEST_F(PropertyMetaInfo, type) { - auto barInfo = createNodeMetaInfo("QtQuick", "Bar"); + auto barInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "Bar"); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, barInfo.id()); auto propertyInfo = nodeInfo.property("bar"); @@ -181,7 +183,7 @@ TEST_F(PropertyMetaInfo, is_enumeration) { TypeTraits traits; traits.isEnum = true; - auto enumInfo = createNodeMetaInfo("QtQuick", "MyEnum", traits); + auto enumInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "MyEnum", traits); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, enumInfo.id()); auto propertyInfo = nodeInfo.property("bar"); @@ -192,7 +194,7 @@ TEST_F(PropertyMetaInfo, is_enumeration) TEST_F(PropertyMetaInfo, is_not_enumeration) { - auto notEnumInfo = createNodeMetaInfo("QtQuick", "NoEnum", {}); + auto notEnumInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "NoEnum", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, notEnumInfo.id()); auto propertyInfo = nodeInfo.property("bar"); @@ -275,7 +277,7 @@ TEST_F(PropertyMetaInfo, cast_to_enumeration) { TypeTraits traits; traits.isEnum = true; - auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "MyEnum", traits); + auto propertyTypeInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "MyEnum", traits); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); Enumeration enumeration{"MyEnum.Foo"}; @@ -288,7 +290,7 @@ TEST_F(PropertyMetaInfo, cast_to_enumeration) TEST_F(PropertyMetaInfo, dont_to_cast_enumeration_if_property_type_is_not_enumeration) { - auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "MyEnum", {}); + auto propertyTypeInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "MyEnum", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); Enumeration enumeration{"MyEnum.Foo"}; @@ -303,7 +305,7 @@ TEST_F(PropertyMetaInfo, dont_to_cast_enumeration_if_value_is_not_Enumeration) { TypeTraits traits; traits.isEnum = true; - auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "MyEnum", traits); + auto propertyTypeInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "MyEnum", traits); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant::fromValue(QString{"enumeration"}); @@ -315,7 +317,7 @@ TEST_F(PropertyMetaInfo, dont_to_cast_enumeration_if_value_is_not_Enumeration) TEST_F(PropertyMetaInfo, cast_to_model_node) { - auto propertyTypeInfo = createNodeMetaInfo("QML", "var", {}); + auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "var", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant::fromValue(model.rootModelNode()); @@ -327,7 +329,7 @@ TEST_F(PropertyMetaInfo, cast_to_model_node) TEST_F(PropertyMetaInfo, cast_to_qvariant_always_returns_the_save_variant_if_the_property_type_is_var) { - auto propertyTypeInfo = createNodeMetaInfo("QML", "var", {}); + auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "var", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant::fromValue(QString{"foo"}); @@ -339,7 +341,7 @@ TEST_F(PropertyMetaInfo, cast_to_qvariant_always_returns_the_save_variant_if_the TEST_F(PropertyMetaInfo, cast_double_to_double) { - auto propertyTypeInfo = createNodeMetaInfo("QML", "double", {}); + auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "double", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant::fromValue(14.2); @@ -351,7 +353,7 @@ TEST_F(PropertyMetaInfo, cast_double_to_double) TEST_F(PropertyMetaInfo, cast_int_to_double_returns_number_variant) { - auto propertyTypeInfo = createNodeMetaInfo("QML", "double", {}); + auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "double", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant::fromValue(14); @@ -363,7 +365,7 @@ TEST_F(PropertyMetaInfo, cast_int_to_double_returns_number_variant) TEST_F(PropertyMetaInfo, cast_default_to_double_returns_zero_variant) { - auto propertyTypeInfo = createNodeMetaInfo("QML", "double", {}); + auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "double", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant(); @@ -375,7 +377,7 @@ TEST_F(PropertyMetaInfo, cast_default_to_double_returns_zero_variant) TEST_F(PropertyMetaInfo, cast_qstring_to_double_returns_zero_variant) { - auto propertyTypeInfo = createNodeMetaInfo("QML", "double", {}); + auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "double", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant::fromValue(QString{"foo"}); @@ -387,7 +389,7 @@ TEST_F(PropertyMetaInfo, cast_qstring_to_double_returns_zero_variant) TEST_F(PropertyMetaInfo, cast_float_to_float) { - auto propertyTypeInfo = createNodeMetaInfo("QML-cppnative", "float", {}); + auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::CppLibrary, "float", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant::fromValue(14.2f); @@ -399,7 +401,7 @@ TEST_F(PropertyMetaInfo, cast_float_to_float) TEST_F(PropertyMetaInfo, cast_int_to_float_returns_number_variant) { - auto propertyTypeInfo = createNodeMetaInfo("QML-cppnative", "float", {}); + auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::CppLibrary, "float", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant::fromValue(14); @@ -411,7 +413,7 @@ TEST_F(PropertyMetaInfo, cast_int_to_float_returns_number_variant) TEST_F(PropertyMetaInfo, cast_default_to_float_returns_zero_variant) { - auto propertyTypeInfo = createNodeMetaInfo("QML-cppnative", "float", {}); + auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::CppLibrary, "float", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant(); @@ -423,7 +425,7 @@ TEST_F(PropertyMetaInfo, cast_default_to_float_returns_zero_variant) TEST_F(PropertyMetaInfo, cast_qstring_to_float_returns_zero_variant) { - auto propertyTypeInfo = createNodeMetaInfo("QML-cppnative", "float", {}); + auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::CppLibrary, "float", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant::fromValue(QString{"foo"}); @@ -435,7 +437,7 @@ TEST_F(PropertyMetaInfo, cast_qstring_to_float_returns_zero_variant) TEST_F(PropertyMetaInfo, cast_int_to_int) { - auto propertyTypeInfo = createNodeMetaInfo("QML", "int", {}); + auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "int", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant::fromValue(14); @@ -447,7 +449,7 @@ TEST_F(PropertyMetaInfo, cast_int_to_int) TEST_F(PropertyMetaInfo, cast_double_to_int_returns_number_variant) { - auto propertyTypeInfo = createNodeMetaInfo("QML", "int", {}); + auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "int", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant::fromValue(14.2); @@ -459,7 +461,7 @@ TEST_F(PropertyMetaInfo, cast_double_to_int_returns_number_variant) TEST_F(PropertyMetaInfo, cast_default_to_int_returns_zero_variant) { - auto propertyTypeInfo = createNodeMetaInfo("QML", "int", {}); + auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "int", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant(); @@ -471,7 +473,7 @@ TEST_F(PropertyMetaInfo, cast_default_to_int_returns_zero_variant) TEST_F(PropertyMetaInfo, cast_qstring_to_int_returns_zero_variant) { - auto propertyTypeInfo = createNodeMetaInfo("QML", "int", {}); + auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "int", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant::fromValue(QString{"foo"}); @@ -483,7 +485,7 @@ TEST_F(PropertyMetaInfo, cast_qstring_to_int_returns_zero_variant) TEST_F(PropertyMetaInfo, cast_bool_to_bool) { - auto propertyTypeInfo = createNodeMetaInfo("QML", "bool", {}); + auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "bool", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant::fromValue(true); @@ -495,7 +497,7 @@ TEST_F(PropertyMetaInfo, cast_bool_to_bool) TEST_F(PropertyMetaInfo, cast_float_to_bool_returns_boolean_variant) { - auto propertyTypeInfo = createNodeMetaInfo("QML", "bool", {}); + auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "bool", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant::fromValue(14.2f); @@ -507,7 +509,7 @@ TEST_F(PropertyMetaInfo, cast_float_to_bool_returns_boolean_variant) TEST_F(PropertyMetaInfo, cast_double_to_bool_returns_boolean_variant) { - auto propertyTypeInfo = createNodeMetaInfo("QML", "bool", {}); + auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "bool", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant::fromValue(14.2); @@ -519,7 +521,7 @@ TEST_F(PropertyMetaInfo, cast_double_to_bool_returns_boolean_variant) TEST_F(PropertyMetaInfo, cast_int_to_bool_returns_boolean_variant) { - auto propertyTypeInfo = createNodeMetaInfo("QML", "bool", {}); + auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "bool", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant::fromValue(14); @@ -531,7 +533,7 @@ TEST_F(PropertyMetaInfo, cast_int_to_bool_returns_boolean_variant) TEST_F(PropertyMetaInfo, cast_long_to_bool_returns_boolean_variant) { - auto propertyTypeInfo = createNodeMetaInfo("QML", "bool", {}); + auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "bool", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant::fromValue(14L); @@ -543,7 +545,7 @@ TEST_F(PropertyMetaInfo, cast_long_to_bool_returns_boolean_variant) TEST_F(PropertyMetaInfo, cast_long_long_to_bool_returns_boolean_variant) { - auto propertyTypeInfo = createNodeMetaInfo("QML", "bool", {}); + auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "bool", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant::fromValue(14LL); @@ -555,7 +557,7 @@ TEST_F(PropertyMetaInfo, cast_long_long_to_bool_returns_boolean_variant) TEST_F(PropertyMetaInfo, cast_default_to_bool_returns_zero_variant) { - auto propertyTypeInfo = createNodeMetaInfo("QML", "bool", {}); + auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "bool", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant(); @@ -567,7 +569,7 @@ TEST_F(PropertyMetaInfo, cast_default_to_bool_returns_zero_variant) TEST_F(PropertyMetaInfo, cast_qstring_to_bool_returns_zero_variant) { - auto propertyTypeInfo = createNodeMetaInfo("QML", "bool", {}); + auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "bool", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant::fromValue(QString{"foo"}); @@ -579,7 +581,7 @@ TEST_F(PropertyMetaInfo, cast_qstring_to_bool_returns_zero_variant) TEST_F(PropertyMetaInfo, cast_string_to_string) { - auto propertyTypeInfo = createNodeMetaInfo("QML", "string", {}); + auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "string", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant::fromValue(QString{"foo"}); @@ -591,7 +593,7 @@ TEST_F(PropertyMetaInfo, cast_string_to_string) TEST_F(PropertyMetaInfo, cast_QByteArray_to_empty_string) { - auto propertyTypeInfo = createNodeMetaInfo("QML", "string", {}); + auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "string", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant::fromValue(QByteArray{"foo"}); @@ -603,7 +605,7 @@ TEST_F(PropertyMetaInfo, cast_QByteArray_to_empty_string) TEST_F(PropertyMetaInfo, cast_int_to_empty_string) { - auto propertyTypeInfo = createNodeMetaInfo("QML", "string", {}); + auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "string", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant::fromValue(14); @@ -615,7 +617,7 @@ TEST_F(PropertyMetaInfo, cast_int_to_empty_string) TEST_F(PropertyMetaInfo, cast_default_to_empty_string) { - auto propertyTypeInfo = createNodeMetaInfo("QML", "string", {}); + auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "string", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant(); @@ -627,7 +629,7 @@ TEST_F(PropertyMetaInfo, cast_default_to_empty_string) TEST_F(PropertyMetaInfo, cast_datatime_to_datetime) { - auto propertyTypeInfo = createNodeMetaInfo("QML", "date", {}); + auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "date", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto dataTime = QDateTime::currentDateTime(); @@ -640,7 +642,7 @@ TEST_F(PropertyMetaInfo, cast_datatime_to_datetime) TEST_F(PropertyMetaInfo, cast_int_to_datetime_returns_default_datetime) { - auto propertyTypeInfo = createNodeMetaInfo("QML", "date", {}); + auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "date", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant::fromValue(14); @@ -652,7 +654,7 @@ TEST_F(PropertyMetaInfo, cast_int_to_datetime_returns_default_datetime) TEST_F(PropertyMetaInfo, cast_string_to_datetime_returns_default_datetime) { - auto propertyTypeInfo = createNodeMetaInfo("QML", "date", {}); + auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "date", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant::fromValue(QString{"Monday"}); @@ -664,7 +666,7 @@ TEST_F(PropertyMetaInfo, cast_string_to_datetime_returns_default_datetime) TEST_F(PropertyMetaInfo, cast_default_to_datetime_returns_default_datetime) { - auto propertyTypeInfo = createNodeMetaInfo("QML", "date", {}); + auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "date", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant(); @@ -676,7 +678,7 @@ TEST_F(PropertyMetaInfo, cast_default_to_datetime_returns_default_datetime) TEST_F(PropertyMetaInfo, cast_url_to_url) { - auto propertyTypeInfo = createNodeMetaInfo("QML", "url", {}); + auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "url", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto url = QUrl("http://www.qt.io/future"); @@ -689,7 +691,7 @@ TEST_F(PropertyMetaInfo, cast_url_to_url) TEST_F(PropertyMetaInfo, cast_string_to_empty_url) { - auto propertyTypeInfo = createNodeMetaInfo("QML", "url", {}); + auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "url", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant::fromValue(QString{"http://www.qt.io/future"}); @@ -701,7 +703,7 @@ TEST_F(PropertyMetaInfo, cast_string_to_empty_url) TEST_F(PropertyMetaInfo, cast_default_to_empty_url) { - auto propertyTypeInfo = createNodeMetaInfo("QML", "url", {}); + auto propertyTypeInfo = createNodeMetaInfo("QML", ModuleKind::QmlLibrary, "url", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant(); @@ -713,7 +715,7 @@ TEST_F(PropertyMetaInfo, cast_default_to_empty_url) TEST_F(PropertyMetaInfo, cast_color_to_color) { - auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "color", {}); + auto propertyTypeInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "color", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto color = QColor(Qt::red); @@ -726,7 +728,7 @@ TEST_F(PropertyMetaInfo, cast_color_to_color) TEST_F(PropertyMetaInfo, cast_string_to_null_color) { - auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "color", {}); + auto propertyTypeInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "color", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant("red"); @@ -738,7 +740,7 @@ TEST_F(PropertyMetaInfo, cast_string_to_null_color) TEST_F(PropertyMetaInfo, cast_int_to_null_color) { - auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "color", {}); + auto propertyTypeInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "color", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant(14); @@ -750,7 +752,7 @@ TEST_F(PropertyMetaInfo, cast_int_to_null_color) TEST_F(PropertyMetaInfo, cast_default_to_null_color) { - auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "color", {}); + auto propertyTypeInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "color", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant(); @@ -762,7 +764,7 @@ TEST_F(PropertyMetaInfo, cast_default_to_null_color) TEST_F(PropertyMetaInfo, cast_vector2d_to_vector2d) { - auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "vector2d", {}); + auto propertyTypeInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "vector2d", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto vector2d = QVector2D{32.2f, 2.2f}; @@ -775,7 +777,7 @@ TEST_F(PropertyMetaInfo, cast_vector2d_to_vector2d) TEST_F(PropertyMetaInfo, cast_string_to_vector2d_returns_an_empty_vector2d) { - auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "vector2d", {}); + auto propertyTypeInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "vector2d", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant(QString{"foo"}); @@ -787,7 +789,7 @@ TEST_F(PropertyMetaInfo, cast_string_to_vector2d_returns_an_empty_vector2d) TEST_F(PropertyMetaInfo, cast_int_to_vector2d_returns_an_empty_vector2d) { - auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "vector2d", {}); + auto propertyTypeInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "vector2d", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant(12); @@ -799,7 +801,7 @@ TEST_F(PropertyMetaInfo, cast_int_to_vector2d_returns_an_empty_vector2d) TEST_F(PropertyMetaInfo, cast_vector3d_to_vector2d_returns_an_empty_vector2d) { - auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "vector2d", {}); + auto propertyTypeInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "vector2d", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant(QVector3D{32.2f, 2.2f, 784.f}); @@ -811,7 +813,7 @@ TEST_F(PropertyMetaInfo, cast_vector3d_to_vector2d_returns_an_empty_vector2d) TEST_F(PropertyMetaInfo, cast_default_to_vector2d_returns_an_empty_vector2d) { - auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "vector2d", {}); + auto propertyTypeInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "vector2d", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant(); @@ -823,7 +825,7 @@ TEST_F(PropertyMetaInfo, cast_default_to_vector2d_returns_an_empty_vector2d) TEST_F(PropertyMetaInfo, cast_vector3d_to_vector3d) { - auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "vector3d", {}); + auto propertyTypeInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "vector3d", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto vector3d = QVector3D{32.2f, 2.2f, 44.4f}; @@ -836,7 +838,7 @@ TEST_F(PropertyMetaInfo, cast_vector3d_to_vector3d) TEST_F(PropertyMetaInfo, cast_string_to_vector3d_returns_an_empty_vector3d) { - auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "vector3d", {}); + auto propertyTypeInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "vector3d", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant(QString{"foo"}); @@ -848,7 +850,7 @@ TEST_F(PropertyMetaInfo, cast_string_to_vector3d_returns_an_empty_vector3d) TEST_F(PropertyMetaInfo, cast_int_to_vector3d_returns_an_empty_vector3d) { - auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "vector3d", {}); + auto propertyTypeInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "vector3d", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant(12); @@ -860,7 +862,7 @@ TEST_F(PropertyMetaInfo, cast_int_to_vector3d_returns_an_empty_vector3d) TEST_F(PropertyMetaInfo, cast_vector4d_to_vector3d_returns_an_empty_vector3d) { - auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "vector3d", {}); + auto propertyTypeInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "vector3d", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant(QVector4D{32.2f, 2.2f, 784.f, 99.f}); @@ -872,7 +874,7 @@ TEST_F(PropertyMetaInfo, cast_vector4d_to_vector3d_returns_an_empty_vector3d) TEST_F(PropertyMetaInfo, cast_default_to_vector3d_returns_an_empty_vector3d) { - auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "vector3d", {}); + auto propertyTypeInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "vector3d", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant(); @@ -884,7 +886,7 @@ TEST_F(PropertyMetaInfo, cast_default_to_vector3d_returns_an_empty_vector3d) TEST_F(PropertyMetaInfo, cast_vector4d_to_vector4d) { - auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "vector4d", {}); + auto propertyTypeInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "vector4d", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto vector4d = QVector4D{32.2f, 2.2f, 44.4f, 23.f}; @@ -897,7 +899,7 @@ TEST_F(PropertyMetaInfo, cast_vector4d_to_vector4d) TEST_F(PropertyMetaInfo, cast_string_to_vector4d_returns_an_empty_vector4d) { - auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "vector4d", {}); + auto propertyTypeInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "vector4d", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant(QString{"foo"}); @@ -909,7 +911,7 @@ TEST_F(PropertyMetaInfo, cast_string_to_vector4d_returns_an_empty_vector4d) TEST_F(PropertyMetaInfo, cast_int_to_vector4d_returns_an_empty_vector4d) { - auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "vector4d", {}); + auto propertyTypeInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "vector4d", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant(12); @@ -921,7 +923,7 @@ TEST_F(PropertyMetaInfo, cast_int_to_vector4d_returns_an_empty_vector4d) TEST_F(PropertyMetaInfo, cast_vector2d_to_vector4d_returns_an_empty_vector4d) { - auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "vector4d", {}); + auto propertyTypeInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "vector4d", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant(QVector2D{32.2f, 2.2f}); @@ -933,7 +935,7 @@ TEST_F(PropertyMetaInfo, cast_vector2d_to_vector4d_returns_an_empty_vector4d) TEST_F(PropertyMetaInfo, cast_default_to_vector4d_returns_an_empty_vector4d) { - auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "vector4d", {}); + auto propertyTypeInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "vector4d", {}); projectStorageMock.createProperty(nodeInfo.id(), "bar", {}, propertyTypeInfo.id()); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant(); @@ -955,7 +957,7 @@ TEST_F(PropertyMetaInfo, default_cast_to_invalid_variant) TEST_F(PropertyMetaInfo, not_existing_property_cast_returns_invalid_value) { - auto propertyTypeInfo = createNodeMetaInfo("QtQuick", "vector4d", {}); + auto propertyTypeInfo = createNodeMetaInfo("QtQuick", ModuleKind::QmlLibrary, "vector4d", {}); auto propertyInfo = nodeInfo.property("bar"); auto value = QVariant(43); diff --git a/tests/unit/tests/unittests/model/CMakeLists.txt b/tests/unit/tests/unittests/model/CMakeLists.txt index 75c81cca6f5..43d6154c504 100644 --- a/tests/unit/tests/unittests/model/CMakeLists.txt +++ b/tests/unit/tests/unittests/model/CMakeLists.txt @@ -7,5 +7,5 @@ extend_qtc_test(unittest modelresourcemanagement-test.cpp modelutils-test.cpp nodelistproperty-test.cpp - + uniquename-test.cpp ) diff --git a/tests/unit/tests/unittests/model/model-test.cpp b/tests/unit/tests/unittests/model/model-test.cpp index bd34a3a6b12..227360c01f6 100644 --- a/tests/unit/tests/unittests/model/model-test.cpp +++ b/tests/unit/tests/unittests/model/model-test.cpp @@ -26,6 +26,7 @@ using QmlDesigner::AbstractProperty; using QmlDesigner::ModelNode; using QmlDesigner::ModelNodes; using QmlDesigner::ModelResourceSet; +using QmlDesigner::Storage::ModuleKind; MATCHER(IsSorted, std::string(negation ? "isn't sorted" : "is sorted")) { @@ -35,7 +36,8 @@ MATCHER(IsSorted, std::string(negation ? "isn't sorted" : "is sorted")) } template -auto IsItemLibraryEntry(const QmlDesigner::NodeMetaInfo &metaInfo, +auto IsItemLibraryEntry(QmlDesigner::TypeId typeId, + QByteArrayView typeName, QStringView name, QStringView iconPath, QStringView category, @@ -46,7 +48,8 @@ auto IsItemLibraryEntry(const QmlDesigner::NodeMetaInfo &metaInfo, ExtraFilePathsMatcher extraFilePathsMatcher) { using QmlDesigner::ItemLibraryEntry; - return AllOf(Property("metaInfo", &ItemLibraryEntry::metaInfo, metaInfo), + return AllOf(Property("typeId", &ItemLibraryEntry::typeId, typeId), + Property("typeName", &ItemLibraryEntry::typeName, typeName), Property("name", &ItemLibraryEntry::name, name), Property("libraryEntryIconPath", &ItemLibraryEntry::libraryEntryIconPath, iconPath), Property("category", &ItemLibraryEntry::category, category), @@ -125,10 +128,10 @@ protected: resourceManagementMock)}; NiceMock viewMock; QmlDesigner::SourceId filePathId = pathCacheMock.sourceId; - QmlDesigner::TypeId itemTypeId = projectStorageMock.typeId(projectStorageMock.moduleId( - "QtQuick"), - "Item", - QmlDesigner::Storage::Version{}); + QmlDesigner::TypeId itemTypeId = projectStorageMock.typeId( + projectStorageMock.moduleId("QtQuick", ModuleKind::QmlLibrary), + "Item", + QmlDesigner::Storage::Version{}); QmlDesigner::ImportedTypeNameId itemTypeNameId = projectStorageMock.createImportedTypeNameId( filePathId, "Item", itemTypeId); ModelNode rootNode; @@ -759,8 +762,8 @@ TEST_F(Model, change_imports_is_synchronizing_imports_with_project_storage) { QmlDesigner::SourceId directoryPathId = QmlDesigner::SourceId::create(2); ON_CALL(pathCacheMock, sourceId(Eq("/path/foo/."))).WillByDefault(Return(directoryPathId)); - auto qtQuickModuleId = projectStorageMock.moduleId("QtQuick"); - auto qtQmlModelsModuleId = projectStorageMock.moduleId("QtQml.Models"); + auto qtQuickModuleId = projectStorageMock.moduleId("QtQuick", ModuleKind::QmlLibrary); + auto qtQmlModelsModuleId = projectStorageMock.moduleId("QtQml.Models", ModuleKind::QmlLibrary); auto qtQuickImport = QmlDesigner::Import::createLibraryImport("QtQuick", "2.1"); auto qtQmlModelsImport = QmlDesigner::Import::createLibraryImport("QtQml.Models"); auto directoryImport = QmlDesigner::Import::createFileImport("foo"); @@ -793,8 +796,8 @@ TEST_F(Model, change_imports_is_adding_import_in_project_storage) { QmlDesigner::SourceId directoryPathId = QmlDesigner::SourceId::create(2); ON_CALL(pathCacheMock, sourceId(Eq("/path/foo/."))).WillByDefault(Return(directoryPathId)); - auto qtQuickModuleId = projectStorageMock.moduleId("QtQuick"); - auto qtQmlModelsModuleId = projectStorageMock.moduleId("QtQml.Models"); + auto qtQuickModuleId = projectStorageMock.moduleId("QtQuick", ModuleKind::QmlLibrary); + auto qtQmlModelsModuleId = projectStorageMock.moduleId("QtQml.Models", ModuleKind::QmlLibrary); auto qtQuickImport = QmlDesigner::Import::createLibraryImport("QtQuick", "2.1"); auto qtQmlModelsImport = QmlDesigner::Import::createLibraryImport("QtQml.Models"); auto directoryImport = QmlDesigner::Import::createFileImport("foo"); @@ -813,7 +816,7 @@ TEST_F(Model, change_imports_is_removing_import_in_project_storage) { QmlDesigner::SourceId directoryPathId = QmlDesigner::SourceId::create(2); ON_CALL(pathCacheMock, sourceId(Eq("/path/foo/."))).WillByDefault(Return(directoryPathId)); - auto qtQmlModelsModuleId = projectStorageMock.moduleId("QtQml.Models"); + auto qtQmlModelsModuleId = projectStorageMock.moduleId("QtQml.Models", ModuleKind::QmlLibrary); auto qtQuickImport = QmlDesigner::Import::createLibraryImport("QtQuick", "2.1"); auto qtQmlModelsImport = QmlDesigner::Import::createLibraryImport("QtQml.Models"); auto directoryImport = QmlDesigner::Import::createFileImport("foo"); @@ -846,8 +849,8 @@ TEST_F(Model, change_imports_is_changing_import_version_with_project_storage) { QmlDesigner::SourceId directoryPathId = QmlDesigner::SourceId::create(2); ON_CALL(pathCacheMock, sourceId(Eq("/path/foo/."))).WillByDefault(Return(directoryPathId)); - auto qtQuickModuleId = projectStorageMock.moduleId("QtQuick"); - auto qtQmlModelsModuleId = projectStorageMock.moduleId("QtQml.Models"); + auto qtQuickModuleId = projectStorageMock.moduleId("QtQuick", ModuleKind::QmlLibrary); + auto qtQmlModelsModuleId = projectStorageMock.moduleId("QtQml.Models", ModuleKind::QmlLibrary); auto qtQuickImport = QmlDesigner::Import::createLibraryImport("QtQuick", "2.1"); auto qtQmlModelsImport = QmlDesigner::Import::createLibraryImport("QtQml.Models"); auto directoryImport = QmlDesigner::Import::createFileImport("foo"); @@ -873,7 +876,7 @@ TEST_F(Model, create_model_node_has_meta_info) TEST_F(Model, create_qualified_model_node_has_meta_info) { auto qtQmlModelsImport = QmlDesigner::Import::createLibraryImport("QtQml.Models", "", "Foo"); - auto qtQmlModelsModulesId = projectStorageMock.moduleId("QtQml.Models"); + auto qtQmlModelsModulesId = projectStorageMock.moduleId("QtQml.Models", ModuleKind::QmlLibrary); auto importId = projectStorageMock.createImportId(qtQmlModelsModulesId, filePathId); auto listModelTypeId = projectStorageMock.typeId(qtQmlModelsModulesId, "ListModel", @@ -913,23 +916,23 @@ TEST_F(Model, meta_info_of_not_existing_type_is_invalid) TEST_F(Model, module_is_valid) { - auto module = model.module("QML"); + auto module = model.module("QML", ModuleKind::QmlLibrary); ASSERT_THAT(module, IsTrue()); } TEST_F(Model, module_returns_always_the_same) { - auto oldModule = model.module("QML"); + auto oldModule = model.module("QML", ModuleKind::QmlLibrary); - auto module = model.module("QML"); + auto module = model.module("QML", ModuleKind::QmlLibrary); ASSERT_THAT(module, oldModule); } TEST_F(Model, get_meta_info_by_module) { - auto module = model.module("QML"); + auto module = model.module("QML", ModuleKind::QmlLibrary); auto metaInfo = model.metaInfo(module, "QtObject"); @@ -938,7 +941,7 @@ TEST_F(Model, get_meta_info_by_module) TEST_F(Model, get_invalid_meta_info_by_module_for_wrong_name) { - auto module = model.module("QML"); + auto module = model.module("QML", ModuleKind::QmlLibrary); auto metaInfo = model.metaInfo(module, "Object"); @@ -947,7 +950,7 @@ TEST_F(Model, get_invalid_meta_info_by_module_for_wrong_name) TEST_F(Model, get_invalid_meta_info_by_module_for_wrong_module) { - auto module = model.module("Qml"); + auto module = model.module("Qml", ModuleKind::QmlLibrary); auto metaInfo = model.metaInfo(module, "Object"); @@ -986,8 +989,8 @@ TEST_F(Model, refresh_callback_is_calling_abstract_view) TEST_F(Model, meta_infos_for_mdoule) { - projectStorageMock.createModule("Foo"); - auto module = model.module("Foo"); + projectStorageMock.createModule("Foo", ModuleKind::QmlLibrary); + auto module = model.module("Foo", ModuleKind::QmlLibrary); auto typeId = projectStorageMock.createObject(module.id(), "Bar"); ON_CALL(projectStorageMock, typeIds(module.id())) .WillByDefault(Return(QVarLengthArray{typeId})); @@ -1000,18 +1003,24 @@ TEST_F(Model, meta_infos_for_mdoule) TEST_F(Model, item_library_entries) { using namespace Qt::StringLiterals; - QmlDesigner::Storage::Info::ItemLibraryEntries storageEntries{ - {itemTypeId, "Item", "/path/to/icon", "basic category", "QtQuick", "It's a item", "/path/to/template"}}; + QmlDesigner::Storage::Info::ItemLibraryEntries storageEntries{{itemTypeId, + "Item", + "Item", + "/path/to/icon", + "basic category", + "QtQuick", + "It's a item", + "/path/to/template"}}; storageEntries.front().properties.emplace_back("x", "double", Sqlite::ValueView::create(1)); storageEntries.front().extraFilePaths.emplace_back("/extra/file/path"); projectStorageMock.setItemLibraryEntries(pathCacheMock.sourceId, storageEntries); - QmlDesigner::NodeMetaInfo metaInfo{itemTypeId, &projectStorageMock}; auto entries = model.itemLibraryEntries(); ASSERT_THAT(entries, ElementsAre( - IsItemLibraryEntry(metaInfo, + IsItemLibraryEntry(itemTypeId, + "Item", u"Item", u"/path/to/icon", u"basic category", diff --git a/tests/unit/tests/unittests/model/modelutils-test.cpp b/tests/unit/tests/unittests/model/modelutils-test.cpp index 5a9e63b60da..2d49c6c94e6 100644 --- a/tests/unit/tests/unittests/model/modelutils-test.cpp +++ b/tests/unit/tests/unittests/model/modelutils-test.cpp @@ -13,6 +13,7 @@ namespace { using QmlDesigner::ModelNode; using QmlDesigner::ModelNodes; +using QmlDesigner::Storage::ModuleKind; class ModelUtils : public ::testing::Test { @@ -20,7 +21,7 @@ protected: NiceMock pathCacheMock{"/path/model.qml"}; QmlDesigner::SourceId sourceId = pathCacheMock.createSourceId("/path/foo.qml"); NiceMock projectStorageMock{pathCacheMock.sourceId}; - QmlDesigner::ModuleId moduleId = projectStorageMock.moduleId("QtQuick"); + QmlDesigner::ModuleId moduleId = projectStorageMock.moduleId("QtQuick", ModuleKind::QmlLibrary); QmlDesigner::Model model{{projectStorageMock, pathCacheMock}, "Item", {QmlDesigner::Import::createLibraryImport("QML"), diff --git a/tests/unit/tests/unittests/model/nodelistproperty-test.cpp b/tests/unit/tests/unittests/model/nodelistproperty-test.cpp index aac2e729a29..238e2f5d158 100644 --- a/tests/unit/tests/unittests/model/nodelistproperty-test.cpp +++ b/tests/unit/tests/unittests/model/nodelistproperty-test.cpp @@ -44,11 +44,6 @@ protected: ~NodeListProperty() { model->detachView(&abstractViewMock); } - void setModuleId(Utils::SmallStringView moduleName, ModuleId moduleId) - { - ON_CALL(projectStorageMock, moduleId(Eq(moduleName))).WillByDefault(Return(moduleId)); - } - void setType(ModuleId moduleId, Utils::SmallStringView typeName, Utils::SmallString defaultPeopertyName) diff --git a/tests/unit/tests/unittests/model/uniquename-test.cpp b/tests/unit/tests/unittests/model/uniquename-test.cpp new file mode 100644 index 00000000000..ca32972b6df --- /dev/null +++ b/tests/unit/tests/unittests/model/uniquename-test.cpp @@ -0,0 +1,112 @@ +// 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 + +#include + +namespace { + +namespace UniqueName = QmlDesigner::UniqueName; + +TEST(UniqueName, generate_returns_same_input_if_predicate_returns_false) +{ + auto pred = [] (const QString &name) -> bool { + return false; + }; + QString name = "abc"; + + QString uniqueName = UniqueName::generate(name, pred); + + ASSERT_THAT(uniqueName, "abc"); +} + +TEST(UniqueName, generateId_returns_properly_formatted_id_when_predicate_is_not_provided) +{ + QString id = " A bc d _"; + + QString uniqueId = UniqueName::generateId(id); + + ASSERT_THAT(uniqueId, "aBcD_"); +} + +TEST(UniqueName, generateId_returns_properly_formatted_id) +{ + auto pred = [] (const QString &id) -> bool { + return false; + }; + QString id = " A bc d _"; + + QString uniqueId = UniqueName::generateId(id, pred); + + ASSERT_THAT(uniqueId, "aBcD_"); +} + +TEST(UniqueName, generateId_returns_properly_formatted_unique_id_when_id_exists) +{ + QStringList existingIds = {"aBcD009", "aBcD010"}; + auto pred = [&] (const QString &id) -> bool { + return existingIds.contains(id); + }; + QString id = " A bc d 0 \t 0 9\n"; + + QString uniqueId = UniqueName::generateId(id, pred); + + ASSERT_THAT(uniqueId, "aBcD011"); +} + +TEST(UniqueName, generateId_properly_handles_dot_separated_words) +{ + auto pred = [&] (const QString &id) -> bool { + return false; + }; + QString id = "Foo.bar*foo"; + + QString uniqueId = UniqueName::generateId(id, pred); + + ASSERT_THAT(uniqueId, "fooBarFoo"); +} + +TEST(UniqueName, generateId_prefixes_with_underscore_if_id_is_a_reserved_word) +{ + auto pred = [&] (const QString &id) -> bool { + return false; + }; + QString id = "for"; + + QString uniqueId = UniqueName::generateId(id, pred); + + ASSERT_THAT(uniqueId, "_for"); +} + +TEST(UniqueName, generateId_prefixes_with_underscore_if_id_is_a_number) +{ + auto pred = [&] (const QString &id) -> bool { + return false; + }; + QString id = "436"; + + QString uniqueId = UniqueName::generateId(id, pred); + + ASSERT_THAT(uniqueId, "_436"); +} + +TEST(UniqueName, generatePath_returns_same_path_when_path_doesnt_exist) +{ + QString path = "<<>>"; + + QString uniquePath = UniqueName::generatePath(path); + + ASSERT_THAT(uniquePath, path); +} + +TEST(UniqueName, generatePath_returns_unique_path_when_path_exists) +{ + QString path = UNITTEST_DIR; + + QString uniquePath = UniqueName::generatePath(path); + + ASSERT_THAT(uniquePath, QString(UNITTEST_DIR).append("1")); +} + +} // namespace diff --git a/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp b/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp index 4034ae58f9f..fc806d73a95 100644 --- a/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp +++ b/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp @@ -5,7 +5,8 @@ #include #include -#include +#include +#include #include #include @@ -28,6 +29,7 @@ using QmlDesigner::PropertyDeclarationId; using QmlDesigner::SourceContextId; using QmlDesigner::SourceId; using QmlDesigner::SourceIds; +using QmlDesigner::Storage::ModuleKind; using QmlDesigner::Storage::Synchronization::SynchronizationPackage; using QmlDesigner::Storage::Synchronization::TypeAnnotations; using QmlDesigner::Storage::TypeTraits; @@ -48,6 +50,12 @@ Storage::Imports operator+(const Storage::Imports &first, return imports; } +auto IsModule(Utils::SmallStringView name, ModuleKind kind) +{ + return AllOf(Field(&QmlDesigner::Storage::Module::name, name), + Field(&QmlDesigner::Storage::Module::kind, kind)); +} + MATCHER_P2(IsSourceContext, id, value, @@ -197,6 +205,23 @@ MATCHER_P4(IsInfoPropertyDeclaration, && propertyDeclaration.traits == traits; } +auto IsUnresolvedTypeId() +{ + return Property(&QmlDesigner::TypeId::internalId, -1); +} + +template +auto IsPrototypeId(const Matcher &matcher) +{ + return Field(&Storage::Synchronization::Type::prototypeId, matcher); +} + +template +auto IsExtensionId(const Matcher &matcher) +{ + return Field(&Storage::Synchronization::Type::extensionId, matcher); +} + class HasNameMatcher { public: @@ -267,21 +292,20 @@ MATCHER_P2(IsInfoType, class ProjectStorage : public testing::Test { protected: - static void SetUpTestSuite() + struct StaticData { - static_database = std::make_unique(":memory:", Sqlite::JournalMode::Memory); + Sqlite::Database database{":memory:", Sqlite::JournalMode::Memory}; + NiceMock errorNotifierMock; + QmlDesigner::ProjectStorage storage{database, errorNotifierMock, database.isInitialized()}; + }; - static_projectStorage = std::make_unique( - *static_database, static_database->isInitialized()); - } + static void SetUpTestSuite() { staticData = std::make_unique(); } - static void TearDownTestSuite() - { - static_projectStorage.reset(); - static_database.reset(); - } + static void TearDownTestSuite() { staticData.reset(); } - ~ProjectStorage() { static_projectStorage->resetForTestsOnly(); } + ProjectStorage() { storage.setErrorNotifier(errorNotifierMock); } + + ~ProjectStorage() { storage.resetForTestsOnly(); } template static auto toValues(Range &&range) @@ -307,6 +331,32 @@ protected: storage.fetchSourceId(sourceContextId3, "bar"); } + auto createVerySimpleSynchronizationPackage() + { + SynchronizationPackage package; + + package.types.push_back(Storage::Synchronization::Type{ + "QQuickItem", + Storage::Synchronization::ImportedType{}, + Storage::Synchronization::ImportedType{}, + TypeTraitsKind::Reference, + sourceId1, + {Storage::Synchronization::ExportedType{qtQuickModuleId, "Item"}, + Storage::Synchronization::ExportedType{qtQuickNativeModuleId, "QQuickItem"}}}); + package.types.push_back(Storage::Synchronization::Type{ + "QObject", + Storage::Synchronization::ImportedType{}, + Storage::Synchronization::ImportedType{}, + TypeTraitsKind::Reference, + sourceId2, + {Storage::Synchronization::ExportedType{qmlModuleId, "Object"}, + Storage::Synchronization::ExportedType{qmlNativeModuleId, "QObject"}}}); + + package.updatedSourceIds = {sourceId1, sourceId2}; + + return package; + } + auto createSimpleSynchronizationPackage() { SynchronizationPackage package; @@ -1134,12 +1184,11 @@ protected: } protected: - inline static std::unique_ptr static_database; - Sqlite::Database &database = *static_database; - inline static std::unique_ptr static_projectStorage; - QmlDesigner::ProjectStorage &storage = *static_projectStorage; - QmlDesigner::SourcePathCache sourcePathCache{ - storage}; + inline static std::unique_ptr staticData; + Sqlite::Database &database = staticData->database; + QmlDesigner::ProjectStorage &storage = staticData->storage; + NiceMock errorNotifierMock; + QmlDesigner::SourcePathCache sourcePathCache{storage}; QmlDesigner::SourcePathView path1{"/path1/to"}; QmlDesigner::SourcePathView path2{"/path2/to"}; QmlDesigner::SourcePathView path3{"/path3/to"}; @@ -1158,15 +1207,15 @@ protected: SourceId sourceIdPath6{sourcePathCache.sourceId(pathPath6)}; SourceId qmlProjectSourceId{sourcePathCache.sourceId("/path1/qmldir")}; SourceId qtQuickProjectSourceId{sourcePathCache.sourceId("/path2/qmldir")}; - ModuleId qmlModuleId{storage.moduleId("Qml")}; - ModuleId qmlNativeModuleId{storage.moduleId("Qml-cppnative")}; - ModuleId qtQuickModuleId{storage.moduleId("QtQuick")}; - ModuleId qtQuickNativeModuleId{storage.moduleId("QtQuick-cppnative")}; - ModuleId pathToModuleId{storage.moduleId("/path/to")}; - ModuleId qtQuick3DModuleId{storage.moduleId("QtQuick3D")}; - ModuleId myModuleModuleId{storage.moduleId("MyModule")}; - ModuleId QMLModuleId{storage.moduleId("QML")}; - ModuleId QMLNativeModuleId{storage.moduleId("QML-cppnative")}; + ModuleId qmlModuleId{storage.moduleId("Qml", ModuleKind::QmlLibrary)}; + ModuleId qmlNativeModuleId{storage.moduleId("Qml", ModuleKind::CppLibrary)}; + ModuleId qtQuickModuleId{storage.moduleId("QtQuick", ModuleKind::QmlLibrary)}; + ModuleId qtQuickNativeModuleId{storage.moduleId("QtQuick", ModuleKind::CppLibrary)}; + ModuleId pathToModuleId{storage.moduleId("/path/to", ModuleKind::PathLibrary)}; + ModuleId qtQuick3DModuleId{storage.moduleId("QtQuick3D", ModuleKind::QmlLibrary)}; + ModuleId myModuleModuleId{storage.moduleId("MyModule", ModuleKind::QmlLibrary)}; + ModuleId QMLModuleId{storage.moduleId("QML", ModuleKind::QmlLibrary)}; + ModuleId QMLNativeModuleId{storage.moduleId("QML", ModuleKind::CppLibrary)}; Storage::Imports importsSourceId1; Storage::Imports importsSourceId2; Storage::Imports importsSourceId3; @@ -1482,22 +1531,193 @@ TEST_F(ProjectStorage, synchronize_types_adds_new_types_with_exported_extension_ "QQuickItem")))))); } -TEST_F(ProjectStorage, synchronize_types_adds_new_types_throws_with_wrong_prototype_name) +TEST_F(ProjectStorage, + synchronize_types_adds_unknown_prototype_which_notifies_about_unresolved_type_name) { auto package{createSimpleSynchronizationPackage()}; package.types[0].prototype = Storage::Synchronization::ImportedType{"Objec"}; - ASSERT_THROW(storage.synchronize(std::move(package)), QmlDesigner::TypeNameDoesNotExists); + EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Objec"), sourceId1)); + + storage.synchronize(std::move(package)); } -TEST_F(ProjectStorage, synchronize_types_adds_new_types_throws_with_wrong_extension_name) +TEST_F(ProjectStorage, synchronize_types_adds_unknown_prototype_as_unresolved_type_id) +{ + auto package{createSimpleSynchronizationPackage()}; + package.types[0].prototype = Storage::Synchronization::ImportedType{"Objec"}; + + storage.synchronize(std::move(package)); + + ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")), + IsPrototypeId(IsUnresolvedTypeId())); +} + +TEST_F(ProjectStorage, synchronize_types_updates_unresolved_prototype_after_exported_type_name_is_added) +{ + auto package{createSimpleSynchronizationPackage()}; + package.types[0].prototype = Storage::Synchronization::ImportedType{"Objec"}; + storage.synchronize(package); + package.types[1].exportedTypes.emplace_back(qmlNativeModuleId, "Objec"); + + storage.synchronize(std::move(package)); + + ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")), + IsPrototypeId(fetchTypeId(sourceId2, "QObject"))); +} + +TEST_F(ProjectStorage, + synchronize_types_updates_prototype_to_unresolved_after_exported_type_name_is_removed) +{ + auto package{createSimpleSynchronizationPackage()}; + package.types[0].prototype = Storage::Synchronization::ImportedType{"Objec"}; + package.types[1].exportedTypes.emplace_back(qmlNativeModuleId, "Objec"); + storage.synchronize(package); + package.types[1].exportedTypes.pop_back(); + + storage.synchronize(std::move(package)); + + ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")), + IsPrototypeId(IsUnresolvedTypeId())); +} + +TEST_F(ProjectStorage, + synchronize_types_updates_unresolved_prototype_indirectly_after_exported_type_name_is_added) +{ + auto package{createSimpleSynchronizationPackage()}; + package.types[0].prototype = Storage::Synchronization::ImportedType{"Objec"}; + storage.synchronize(package); + package.types[1].exportedTypes.emplace_back(qmlNativeModuleId, "Objec"); + package.types.erase(package.types.begin()); + package.updatedSourceIds = {sourceId2}; + + storage.synchronize(std::move(package)); + + ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")), + IsPrototypeId(fetchTypeId(sourceId2, "QObject"))); +} + +TEST_F(ProjectStorage, + synchronize_types_updates_prototype_indirectly_to_unresolved_after_exported_type_name_is_removed) +{ + auto package{createSimpleSynchronizationPackage()}; + package.types[0].prototype = Storage::Synchronization::ImportedType{"Objec"}; + package.types[1].exportedTypes.emplace_back(qmlNativeModuleId, "Objec"); + storage.synchronize(package); + package.types[1].exportedTypes.pop_back(); + package.types.erase(package.types.begin()); + package.updatedSourceIds = {sourceId2}; + + storage.synchronize(std::move(package)); + + ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")), + IsPrototypeId(IsUnresolvedTypeId())); +} + +TEST_F(ProjectStorage, + synchronize_types_updates_prototype_indirectly_to_unresolved_after_exported_type_name_is_removed_notifies_type_name_cannot_be_resolved) +{ + auto package{createSimpleSynchronizationPackage()}; + package.types[0].prototype = Storage::Synchronization::ImportedType{"Objec"}; + package.types[1].exportedTypes.emplace_back(qmlNativeModuleId, "Objec"); + storage.synchronize(package); + package.types[1].exportedTypes.pop_back(); + package.types.erase(package.types.begin()); + package.updatedSourceIds = {sourceId2}; + + EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Objec"), sourceId1)); + + storage.synchronize(std::move(package)); +} + +TEST_F(ProjectStorage, synchronize_types_updates_unresolved_extension_after_exported_type_name_is_added) +{ + auto package{createSimpleSynchronizationPackage()}; + package.types[0].extension = Storage::Synchronization::ImportedType{"Objec"}; + storage.synchronize(package); + package.types[1].exportedTypes.emplace_back(qmlNativeModuleId, "Objec"); + + storage.synchronize(std::move(package)); + + ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")), + IsExtensionId(fetchTypeId(sourceId2, "QObject"))); +} + +TEST_F(ProjectStorage, + synchronize_types_updates_extension_to_unresolved_after_exported_type_name_is_removed) +{ + auto package{createSimpleSynchronizationPackage()}; + package.types[0].extension = Storage::Synchronization::ImportedType{"Objec"}; + package.types[1].exportedTypes.emplace_back(qmlNativeModuleId, "Objec"); + storage.synchronize(package); + package.types[1].exportedTypes.pop_back(); + + storage.synchronize(std::move(package)); + + ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")), + IsExtensionId(IsUnresolvedTypeId())); +} + +TEST_F(ProjectStorage, + synchronize_types_updates_unresolved_extension_indirectly_after_exported_type_name_is_added) +{ + auto package{createSimpleSynchronizationPackage()}; + package.types[0].extension = Storage::Synchronization::ImportedType{"Objec"}; + storage.synchronize(package); + package.types[1].exportedTypes.emplace_back(qmlNativeModuleId, "Objec"); + package.types.erase(package.types.begin()); + package.updatedSourceIds = {sourceId2}; + + storage.synchronize(std::move(package)); + + ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")), + IsExtensionId(fetchTypeId(sourceId2, "QObject"))); +} + +TEST_F(ProjectStorage, + synchronize_types_updates_invalid_extension_indirectly_after_exported_type_name_is_removed) +{ + auto package{createSimpleSynchronizationPackage()}; + package.types[0].extension = Storage::Synchronization::ImportedType{"Objec"}; + package.types[1].exportedTypes.emplace_back(qmlNativeModuleId, "Objec"); + storage.synchronize(package); + package.types[1].exportedTypes.pop_back(); + package.types.erase(package.types.begin()); + package.updatedSourceIds = {sourceId2}; + + storage.synchronize(std::move(package)); + + ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")), + IsExtensionId(IsUnresolvedTypeId())); +} + +TEST_F(ProjectStorage, + synchronize_types_updates_extension_indirectly_to_unresolved_after_exported_type_name_is_removed_notifies_type_name_cannot_be_resolved) +{ + auto package{createSimpleSynchronizationPackage()}; + package.types[0].extension = Storage::Synchronization::ImportedType{"Objec"}; + package.types[1].exportedTypes.emplace_back(qmlNativeModuleId, "Objec"); + storage.synchronize(package); + package.types[1].exportedTypes.pop_back(); + package.types.erase(package.types.begin()); + package.updatedSourceIds = {sourceId2}; + + EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Objec"), sourceId1)); + + storage.synchronize(std::move(package)); +} + +TEST_F(ProjectStorage, synchronize_types_adds_extension_which_notifies_about_unresolved_type_name) { auto package{createSimpleSynchronizationPackage()}; package.types[0].extension = Storage::Synchronization::ImportedType{"Objec"}; - ASSERT_THROW(storage.synchronize(std::move(package)), QmlDesigner::TypeNameDoesNotExists); + EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Objec"), sourceId1)); + + storage.synchronize(std::move(package)); } + TEST_F(ProjectStorage, synchronize_types_adds_new_types_with_missing_module) { auto package{createSimpleSynchronizationPackage()}; @@ -1770,7 +1990,7 @@ TEST_F(ProjectStorage, synchronize_types_add_qualified_extension) IsExportedType(qtQuickNativeModuleId, "QQuickItem")))))); } -TEST_F(ProjectStorage, synchronize_types_throws_for_missing_prototype) +TEST_F(ProjectStorage, synchronize_types_notifies_cannot_resolve_for_missing_prototype) { auto package{createSimpleSynchronizationPackage()}; package.types = {Storage::Synchronization::Type{ @@ -1782,10 +2002,12 @@ TEST_F(ProjectStorage, synchronize_types_throws_for_missing_prototype) {Storage::Synchronization::ExportedType{qtQuickModuleId, "Item"}, Storage::Synchronization::ExportedType{qtQuickNativeModuleId, "QQuickItem"}}}}; - ASSERT_THROW(storage.synchronize(package), QmlDesigner::TypeNameDoesNotExists); + EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("QObject"), sourceId1)); + + storage.synchronize(package); } -TEST_F(ProjectStorage, synchronize_types_throws_for_missing_extension) +TEST_F(ProjectStorage, synchronize_types_notifies_cannot_resolve_for_missing_extension) { auto package{createSimpleSynchronizationPackage()}; package.types = {Storage::Synchronization::Type{ @@ -1797,7 +2019,9 @@ TEST_F(ProjectStorage, synchronize_types_throws_for_missing_extension) {Storage::Synchronization::ExportedType{qtQuickModuleId, "Item"}, Storage::Synchronization::ExportedType{qtQuickNativeModuleId, "QQuickItem"}}}}; - ASSERT_THROW(storage.synchronize(package), QmlDesigner::TypeNameDoesNotExists); + EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("QObject"), sourceId1)); + + storage.synchronize(package); } TEST_F(ProjectStorage, synchronize_types_throws_for_invalid_module) @@ -2915,7 +3139,7 @@ TEST_F(ProjectStorage, fetch_invalid_type_id_by_impor_ids_and_exported_name_if_n { auto package{createSimpleSynchronizationPackage()}; storage.synchronize(package); - auto qtQuickModuleId = storage.moduleId("QtQuick"); + auto qtQuickModuleId = storage.moduleId("QtQuick", ModuleKind::QmlLibrary); auto typeId = storage.fetchTypeIdByModuleIdsAndExportedName({qtQuickModuleId}, "Object"); @@ -3666,7 +3890,8 @@ TEST_F(ProjectStorage, change_extension_type_module_id) TypeTraitsKind::Reference))); } -TEST_F(ProjectStorage, change_qualified_prototype_type_module_id_throws) +TEST_F(ProjectStorage, + change_qualified_prototype_type_module_id_notifies_that_type_name_cannot_be_resolved) { auto package{createSimpleSynchronizationPackage()}; package.types[0].prototype = Storage::Synchronization::QualifiedImportedType{ @@ -3674,12 +3899,12 @@ TEST_F(ProjectStorage, change_qualified_prototype_type_module_id_throws) storage.synchronize(package); package.types[1].exportedTypes[0].moduleId = qtQuickModuleId; - ASSERT_THROW(storage.synchronize( - SynchronizationPackage{importsSourceId2, {package.types[1]}, {sourceId2}}), - QmlDesigner::TypeNameDoesNotExists); + EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Object"), sourceId1)); + + storage.synchronize(SynchronizationPackage{importsSourceId2, {package.types[1]}, {sourceId2}}); } -TEST_F(ProjectStorage, change_qualified_extension_type_module_id_throws) +TEST_F(ProjectStorage, change_qualified_extension_type_module_id_notifies_cannot_resolve) { auto package{createSimpleSynchronizationPackage()}; std::swap(package.types.front().extension, package.types.front().prototype); @@ -3688,9 +3913,9 @@ TEST_F(ProjectStorage, change_qualified_extension_type_module_id_throws) storage.synchronize(package); package.types[1].exportedTypes[0].moduleId = qtQuickModuleId; - ASSERT_THROW(storage.synchronize( - SynchronizationPackage{importsSourceId2, {package.types[1]}, {sourceId2}}), - QmlDesigner::TypeNameDoesNotExists); + EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Object"), sourceId1)); + + storage.synchronize(SynchronizationPackage{importsSourceId2, {package.types[1]}, {sourceId2}}); } TEST_F(ProjectStorage, change_qualified_prototype_type_module_id) @@ -3794,9 +4019,9 @@ TEST_F(ProjectStorage, change_prototype_type_name_throws_for_wrong_native_protot package.types[1].exportedTypes[2].name = "QObject3"; package.types[1].typeName = "QObject3"; - ASSERT_THROW(storage.synchronize( - SynchronizationPackage{importsSourceId2, {package.types[1]}, {sourceId2}}), - QmlDesigner::TypeNameDoesNotExists); + EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("QObject"), sourceId1)); + + storage.synchronize(SynchronizationPackage{importsSourceId2, {package.types[1]}, {sourceId2}}); } TEST_F(ProjectStorage, change_extension_type_name_throws_for_wrong_native_extension_type_name) @@ -3809,9 +4034,9 @@ TEST_F(ProjectStorage, change_extension_type_name_throws_for_wrong_native_extens package.types[1].exportedTypes[2].name = "QObject3"; package.types[1].typeName = "QObject3"; - ASSERT_THROW(storage.synchronize( - SynchronizationPackage{importsSourceId2, {package.types[1]}, {sourceId2}}), - QmlDesigner::TypeNameDoesNotExists); + EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("QObject"), sourceId1)); + + storage.synchronize(SynchronizationPackage{importsSourceId2, {package.types[1]}, {sourceId2}}); } TEST_F(ProjectStorage, throw_for_prototype_chain_cycles) @@ -4113,23 +4338,29 @@ TEST_F(ProjectStorage, qualified_extension) TypeTraitsKind::Reference))); } -TEST_F(ProjectStorage, qualified_prototype_upper_down_the_module_chain_throws) +TEST_F(ProjectStorage, + qualified_prototype_upper_down_the_module_chain_notifies_type_name_cannot_be_resolved) { auto package{createSimpleSynchronizationPackage()}; package.types[0].prototype = Storage::Synchronization::QualifiedImportedType{ "Object", Storage::Import{qtQuickModuleId, Storage::Version{}, sourceId1}}; - ASSERT_THROW(storage.synchronize(package), QmlDesigner::TypeNameDoesNotExists); + EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Object"), sourceId1)); + + storage.synchronize(package); } -TEST_F(ProjectStorage, qualified_extension_upper_down_the_module_chain_throws) +TEST_F(ProjectStorage, + qualified_extension_upper_down_the_module_chain_notifies_type_name_cannot_be_resolved) { auto package{createSimpleSynchronizationPackage()}; std::swap(package.types.front().extension, package.types.front().prototype); package.types[0].extension = Storage::Synchronization::QualifiedImportedType{ "Object", Storage::Import{qtQuickModuleId, Storage::Version{}, sourceId1}}; - ASSERT_THROW(storage.synchronize(package), QmlDesigner::TypeNameDoesNotExists); + EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Object"), sourceId1)); + + storage.synchronize(package); } TEST_F(ProjectStorage, qualified_prototype_upper_in_the_module_chain) @@ -4185,7 +4416,7 @@ TEST_F(ProjectStorage, qualified_extension_upper_in_the_module_chain) TypeTraitsKind::Reference))); } -TEST_F(ProjectStorage, qualified_prototype_with_wrong_version_throws) +TEST_F(ProjectStorage, qualified_prototype_with_wrong_version_notifies_type_name_cannot_be_resolved) { auto package{createSimpleSynchronizationPackage()}; package.types[0].prototype = Storage::Synchronization::QualifiedImportedType{ @@ -4201,10 +4432,12 @@ TEST_F(ProjectStorage, qualified_prototype_with_wrong_version_throws) package.imports.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId3); package.updatedSourceIds.push_back(sourceId3); - ASSERT_THROW(storage.synchronize(package), QmlDesigner::TypeNameDoesNotExists); + EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Object"), sourceId1)); + + storage.synchronize(package); } -TEST_F(ProjectStorage, qualified_extension_with_wrong_version_throws) +TEST_F(ProjectStorage, qualified_extension_with_wrong_version_notifies_type_name_cannot_be_resolved) { auto package{createSimpleSynchronizationPackage()}; std::swap(package.types.front().extension, package.types.front().prototype); @@ -4221,7 +4454,9 @@ TEST_F(ProjectStorage, qualified_extension_with_wrong_version_throws) package.imports.emplace_back(qtQuickModuleId, Storage::Version{}, sourceId3); package.updatedSourceIds.push_back(sourceId3); - ASSERT_THROW(storage.synchronize(package), QmlDesigner::TypeNameDoesNotExists); + EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Object"), sourceId1)); + + storage.synchronize(package); } TEST_F(ProjectStorage, qualified_prototype_with_version) @@ -4334,23 +4569,29 @@ TEST_F(ProjectStorage, qualified_extension_with_version_in_the_proto_type_chain) TypeTraitsKind::Reference))); } -TEST_F(ProjectStorage, qualified_prototype_with_version_down_the_proto_type_chain_throws) +TEST_F(ProjectStorage, + qualified_prototype_with_version_down_the_proto_type_chain_notifies_type_name_cannot_be_resolved) { auto package{createSimpleSynchronizationPackage()}; package.types[0].prototype = Storage::Synchronization::QualifiedImportedType{ "Object", Storage::Import{qtQuickModuleId, Storage::Version{2}, sourceId1}}; - ASSERT_THROW(storage.synchronize(package), QmlDesigner::TypeNameDoesNotExists); + EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Object"), sourceId1)); + + storage.synchronize(package); } -TEST_F(ProjectStorage, qualified_extension_with_version_down_the_proto_type_chain_throws) +TEST_F(ProjectStorage, + qualified_extension_with_version_down_the_proto_type_chain_notifies_type_name_cannot_be_resolved) { auto package{createSimpleSynchronizationPackage()}; std::swap(package.types.front().extension, package.types.front().prototype); package.types[0].extension = Storage::Synchronization::QualifiedImportedType{ "Object", Storage::Import{qtQuickModuleId, Storage::Version{2}, sourceId1}}; - ASSERT_THROW(storage.synchronize(package), QmlDesigner::TypeNameDoesNotExists); + EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Object"), sourceId1)); + + storage.synchronize(package); } TEST_F(ProjectStorage, qualified_property_declaration_type_name) @@ -4655,7 +4896,7 @@ TEST_F(ProjectStorage, fetch_by_major_version_and_minor_version_for_qualified_im } TEST_F(ProjectStorage, - fetch_by_major_version_and_minor_version_for_imported_type_if_minor_version_is_not_exported_throws) + fetch_by_major_version_and_minor_version_for_imported_type_if_minor_version_is_not_exported_notifies_type_name_cannot_be_resolved) { auto package{createSynchronizationPackageWithVersions()}; storage.synchronize(package); @@ -4669,12 +4910,13 @@ TEST_F(ProjectStorage, Storage::Version{}}}}; Storage::Import import{qmlModuleId, Storage::Version{1, 1}, sourceId2}; - ASSERT_THROW(storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}), - QmlDesigner::TypeNameDoesNotExists); + EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Object"), sourceId2)); + + storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}); } TEST_F(ProjectStorage, - fetch_by_major_version_and_minor_version_for_qualified_imported_type_if_minor_version_is_not_exported_throws) + fetch_by_major_version_and_minor_version_for_qualified_imported_type_if_minor_version_is_not_exported_notifies_type_name_cannot_be_resolved) { auto package{createSynchronizationPackageWithVersions()}; storage.synchronize(package); @@ -4687,11 +4929,12 @@ TEST_F(ProjectStorage, sourceId2, {Storage::Synchronization::ExportedType{qtQuickModuleId, "Item", Storage::Version{}}}}; - ASSERT_THROW(storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}), - QmlDesigner::TypeNameDoesNotExists); + EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Object"), sourceId2)); + + storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}); } -TEST_F(ProjectStorage, fetch_low_minor_version_for_imported_type_throws) +TEST_F(ProjectStorage, fetch_low_minor_version_for_imported_type_notifies_type_name_cannot_be_resolved) { auto package{createSynchronizationPackageWithVersions()}; storage.synchronize(package); @@ -4705,11 +4948,13 @@ TEST_F(ProjectStorage, fetch_low_minor_version_for_imported_type_throws) Storage::Version{}}}}; Storage::Import import{qmlModuleId, Storage::Version{1, 1}, sourceId2}; - ASSERT_THROW(storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}), - QmlDesigner::TypeNameDoesNotExists); + EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Obj"), sourceId2)); + + storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}); } -TEST_F(ProjectStorage, fetch_low_minor_version_for_qualified_imported_type_throws) +TEST_F(ProjectStorage, + fetch_low_minor_version_for_qualified_imported_type_notifies_type_name_cannot_be_resolved) { auto package{createSynchronizationPackageWithVersions()}; storage.synchronize(package); @@ -4722,8 +4967,9 @@ TEST_F(ProjectStorage, fetch_low_minor_version_for_qualified_imported_type_throw sourceId2, {Storage::Synchronization::ExportedType{qtQuickModuleId, "Item", Storage::Version{}}}}; - ASSERT_THROW(storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}), - QmlDesigner::TypeNameDoesNotExists); + EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Obj"), sourceId2)); + + storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}); } TEST_F(ProjectStorage, fetch_higher_minor_version_for_imported_type) @@ -4771,7 +5017,8 @@ TEST_F(ProjectStorage, fetch_higher_minor_version_for_qualified_imported_type) TypeTraitsKind::Reference))); } -TEST_F(ProjectStorage, fetch_different_major_version_for_imported_type_throws) +TEST_F(ProjectStorage, + fetch_different_major_version_for_imported_type_notifies_type_name_cannot_be_resolved) { auto package{createSynchronizationPackageWithVersions()}; storage.synchronize(package); @@ -4785,11 +5032,13 @@ TEST_F(ProjectStorage, fetch_different_major_version_for_imported_type_throws) Storage::Version{}}}}; Storage::Import import{qmlModuleId, Storage::Version{3, 1}, sourceId2}; - ASSERT_THROW(storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}), - QmlDesigner::TypeNameDoesNotExists); + EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Obj"), sourceId2)); + + storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}); } -TEST_F(ProjectStorage, fetch_different_major_version_for_qualified_imported_type_throws) +TEST_F(ProjectStorage, + fetch_different_major_version_for_qualified_imported_type_notifies_type_name_cannot_be_resolved) { auto package{createSynchronizationPackageWithVersions()}; storage.synchronize(package); @@ -4802,8 +5051,9 @@ TEST_F(ProjectStorage, fetch_different_major_version_for_qualified_imported_type sourceId2, {Storage::Synchronization::ExportedType{qtQuickModuleId, "Item", Storage::Version{}}}}; - ASSERT_THROW(storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}), - QmlDesigner::TypeNameDoesNotExists); + EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Obj"), sourceId2)); + + storage.synchronize(SynchronizationPackage{{import}, {type}, {sourceId2}}); } TEST_F(ProjectStorage, fetch_other_type_by_different_version_for_imported_type) @@ -5065,262 +5315,314 @@ TEST_F(ProjectStorage, minimal_updates) TEST_F(ProjectStorage, get_module_id) { - auto id = storage.moduleId("Qml"); + auto id = storage.moduleId("Qml", ModuleKind::QmlLibrary); ASSERT_TRUE(id); } TEST_F(ProjectStorage, get_same_module_id_again) { - auto initialId = storage.moduleId("Qml"); + auto initialId = storage.moduleId("Qml", ModuleKind::QmlLibrary); - auto id = storage.moduleId("Qml"); + auto id = storage.moduleId("Qml", ModuleKind::QmlLibrary); ASSERT_THAT(id, Eq(initialId)); } -TEST_F(ProjectStorage, module_name_throws_if_id_is_invalid) +TEST_F(ProjectStorage, different_module_kind_returns_different_id) { - ASSERT_THROW(storage.moduleName(ModuleId{}), QmlDesigner::ModuleDoesNotExists); + auto qmlId = storage.moduleId("Qml", ModuleKind::QmlLibrary); + + auto cppId = storage.moduleId("Qml", ModuleKind::CppLibrary); + + ASSERT_THAT(cppId, Ne(qmlId)); } -TEST_F(ProjectStorage, module_name_throws_if_id_does_not_exists) +TEST_F(ProjectStorage, module_throws_if_id_is_invalid) { - ASSERT_THROW(storage.moduleName(ModuleId::create(222)), QmlDesigner::ModuleDoesNotExists); + ASSERT_THROW(storage.module(ModuleId{}), QmlDesigner::ModuleDoesNotExists); } -TEST_F(ProjectStorage, get_module_name) +TEST_F(ProjectStorage, module_throws_if_id_does_not_exists) { - auto id = storage.moduleId("Qml"); + ASSERT_THROW(storage.module(ModuleId::create(222)), QmlDesigner::ModuleDoesNotExists); +} - auto name = storage.moduleName(id); +TEST_F(ProjectStorage, get_module) +{ + auto id = storage.moduleId("Qml", ModuleKind::QmlLibrary); - ASSERT_THAT(name, Eq("Qml")); + auto module = storage.module(id); + + ASSERT_THAT(module, IsModule("Qml", ModuleKind::QmlLibrary)); } TEST_F(ProjectStorage, populate_module_cache) { - auto id = storage.moduleId("Qml"); + auto id = storage.moduleId("Qml", ModuleKind::QmlLibrary); - QmlDesigner::ProjectStorage newStorage{database, database.isInitialized()}; + QmlDesigner::ProjectStorage newStorage{database, errorNotifierMock, database.isInitialized()}; - ASSERT_THAT(newStorage.moduleName(id), Eq("Qml")); + ASSERT_THAT(newStorage.module(id), IsModule("Qml", ModuleKind::QmlLibrary)); } -TEST_F(ProjectStorage, add_project_dataes) +TEST_F(ProjectStorage, add_directory_infoes) { - Storage::Synchronization::ProjectData projectData1{qmlProjectSourceId, + Storage::Synchronization::DirectoryInfo directoryInfo1{qmlProjectSourceId, sourceId1, qmlModuleId, Storage::Synchronization::FileType::QmlDocument}; - Storage::Synchronization::ProjectData projectData2{qmlProjectSourceId, + Storage::Synchronization::DirectoryInfo directoryInfo2{qmlProjectSourceId, sourceId2, qmlModuleId, Storage::Synchronization::FileType::QmlDocument}; - Storage::Synchronization::ProjectData projectData3{qtQuickProjectSourceId, + Storage::Synchronization::DirectoryInfo directoryInfo3{qtQuickProjectSourceId, sourceId3, qtQuickModuleId, Storage::Synchronization::FileType::QmlTypes}; storage.synchronize(SynchronizationPackage{{qmlProjectSourceId, qtQuickProjectSourceId}, - {projectData1, projectData2, projectData3}}); + {directoryInfo1, directoryInfo2, directoryInfo3}}); - ASSERT_THAT(storage.fetchProjectDatas({qmlProjectSourceId, qtQuickProjectSourceId}), - UnorderedElementsAre(projectData1, projectData2, projectData3)); + ASSERT_THAT(storage.fetchDirectoryInfos({qmlProjectSourceId, qtQuickProjectSourceId}), + UnorderedElementsAre(directoryInfo1, directoryInfo2, directoryInfo3)); } -TEST_F(ProjectStorage, remove_project_data) +TEST_F(ProjectStorage, remove_directory_info) { - Storage::Synchronization::ProjectData projectData1{qmlProjectSourceId, + Storage::Synchronization::DirectoryInfo directoryInfo1{qmlProjectSourceId, sourceId1, qmlModuleId, Storage::Synchronization::FileType::QmlDocument}; - Storage::Synchronization::ProjectData projectData2{qmlProjectSourceId, + Storage::Synchronization::DirectoryInfo directoryInfo2{qmlProjectSourceId, sourceId2, qmlModuleId, Storage::Synchronization::FileType::QmlDocument}; - Storage::Synchronization::ProjectData projectData3{qtQuickProjectSourceId, + Storage::Synchronization::DirectoryInfo directoryInfo3{qtQuickProjectSourceId, sourceId3, qtQuickModuleId, Storage::Synchronization::FileType::QmlTypes}; storage.synchronize(SynchronizationPackage{{qmlProjectSourceId, qtQuickProjectSourceId}, - {projectData1, projectData2, projectData3}}); + {directoryInfo1, directoryInfo2, directoryInfo3}}); storage.synchronize( - SynchronizationPackage{{qmlProjectSourceId, qtQuickProjectSourceId}, {projectData1}}); + SynchronizationPackage{{qmlProjectSourceId, qtQuickProjectSourceId}, {directoryInfo1}}); - ASSERT_THAT(storage.fetchProjectDatas({qmlProjectSourceId, qtQuickProjectSourceId}), - UnorderedElementsAre(projectData1)); + ASSERT_THAT(storage.fetchDirectoryInfos({qmlProjectSourceId, qtQuickProjectSourceId}), + UnorderedElementsAre(directoryInfo1)); } -TEST_F(ProjectStorage, update_project_data_file_type) +TEST_F(ProjectStorage, update_directory_info_file_type) { - Storage::Synchronization::ProjectData projectData1{qmlProjectSourceId, + Storage::Synchronization::DirectoryInfo directoryInfo1{qmlProjectSourceId, sourceId1, qmlModuleId, Storage::Synchronization::FileType::QmlDocument}; - Storage::Synchronization::ProjectData projectData2{qmlProjectSourceId, + Storage::Synchronization::DirectoryInfo directoryInfo2{qmlProjectSourceId, sourceId2, qmlModuleId, Storage::Synchronization::FileType::QmlDocument}; - Storage::Synchronization::ProjectData projectData2b{qmlProjectSourceId, + Storage::Synchronization::DirectoryInfo directoryInfo2b{qmlProjectSourceId, sourceId2, qmlModuleId, Storage::Synchronization::FileType::QmlTypes}; - Storage::Synchronization::ProjectData projectData3{qtQuickProjectSourceId, + Storage::Synchronization::DirectoryInfo directoryInfo3{qtQuickProjectSourceId, sourceId3, qtQuickModuleId, Storage::Synchronization::FileType::QmlTypes}; storage.synchronize(SynchronizationPackage{{qmlProjectSourceId, qtQuickProjectSourceId}, - {projectData1, projectData2, projectData3}}); + {directoryInfo1, directoryInfo2, directoryInfo3}}); - storage.synchronize(SynchronizationPackage{{qmlProjectSourceId}, {projectData1, projectData2b}}); + storage.synchronize(SynchronizationPackage{{qmlProjectSourceId}, {directoryInfo1, directoryInfo2b}}); - ASSERT_THAT(storage.fetchProjectDatas({qmlProjectSourceId, qtQuickProjectSourceId}), - UnorderedElementsAre(projectData1, projectData2b, projectData3)); + ASSERT_THAT(storage.fetchDirectoryInfos({qmlProjectSourceId, qtQuickProjectSourceId}), + UnorderedElementsAre(directoryInfo1, directoryInfo2b, directoryInfo3)); } -TEST_F(ProjectStorage, update_project_data_module_id) +TEST_F(ProjectStorage, update_directory_info_module_id) { - Storage::Synchronization::ProjectData projectData1{qmlProjectSourceId, + Storage::Synchronization::DirectoryInfo directoryInfo1{qmlProjectSourceId, sourceId1, qmlModuleId, Storage::Synchronization::FileType::QmlDocument}; - Storage::Synchronization::ProjectData projectData2{qmlProjectSourceId, + Storage::Synchronization::DirectoryInfo directoryInfo2{qmlProjectSourceId, sourceId3, qmlModuleId, Storage::Synchronization::FileType::QmlDocument}; - Storage::Synchronization::ProjectData projectData2b{qmlProjectSourceId, + Storage::Synchronization::DirectoryInfo directoryInfo2b{qmlProjectSourceId, sourceId3, qtQuickModuleId, Storage::Synchronization::FileType::QmlDocument}; - Storage::Synchronization::ProjectData projectData3{qtQuickProjectSourceId, + Storage::Synchronization::DirectoryInfo directoryInfo3{qtQuickProjectSourceId, sourceId2, qtQuickModuleId, Storage::Synchronization::FileType::QmlTypes}; storage.synchronize(SynchronizationPackage{{qmlProjectSourceId, qtQuickProjectSourceId}, - {projectData1, projectData2, projectData3}}); + {directoryInfo1, directoryInfo2, directoryInfo3}}); - storage.synchronize(SynchronizationPackage{{qmlProjectSourceId}, {projectData1, projectData2b}}); + storage.synchronize(SynchronizationPackage{{qmlProjectSourceId}, {directoryInfo1, directoryInfo2b}}); - ASSERT_THAT(storage.fetchProjectDatas({qmlProjectSourceId, qtQuickProjectSourceId}), - UnorderedElementsAre(projectData1, projectData2b, projectData3)); + ASSERT_THAT(storage.fetchDirectoryInfos({qmlProjectSourceId, qtQuickProjectSourceId}), + UnorderedElementsAre(directoryInfo1, directoryInfo2b, directoryInfo3)); } -TEST_F(ProjectStorage, throw_for_invalid_source_id_in_project_data) +TEST_F(ProjectStorage, throw_for_invalid_source_id_in_directory_info) { - Storage::Synchronization::ProjectData projectData1{qmlProjectSourceId, + Storage::Synchronization::DirectoryInfo directoryInfo1{qmlProjectSourceId, SourceId{}, qmlModuleId, Storage::Synchronization::FileType::QmlDocument}; - ASSERT_THROW(storage.synchronize(SynchronizationPackage{{qmlProjectSourceId}, {projectData1}}), - QmlDesigner::ProjectDataHasInvalidSourceId); + ASSERT_THROW(storage.synchronize(SynchronizationPackage{{qmlProjectSourceId}, {directoryInfo1}}), + QmlDesigner::DirectoryInfoHasInvalidSourceId); } -TEST_F(ProjectStorage, insert_project_data_with_invalid_module_id) +TEST_F(ProjectStorage, insert_directory_info_with_invalid_module_id) { - Storage::Synchronization::ProjectData projectData1{qmlProjectSourceId, + Storage::Synchronization::DirectoryInfo directoryInfo1{qmlProjectSourceId, sourceId1, ModuleId{}, Storage::Synchronization::FileType::QmlDocument}; - storage.synchronize(SynchronizationPackage{{qmlProjectSourceId}, {projectData1}}); + storage.synchronize(SynchronizationPackage{{qmlProjectSourceId}, {directoryInfo1}}); - ASSERT_THAT(storage.fetchProjectDatas({qmlProjectSourceId, qtQuickProjectSourceId}), - UnorderedElementsAre(projectData1)); + ASSERT_THAT(storage.fetchDirectoryInfos({qmlProjectSourceId, qtQuickProjectSourceId}), + UnorderedElementsAre(directoryInfo1)); } -TEST_F(ProjectStorage, update_project_data_with_invalid_module_id) +TEST_F(ProjectStorage, update_directory_info_with_invalid_module_id) { - Storage::Synchronization::ProjectData projectData1{qmlProjectSourceId, + Storage::Synchronization::DirectoryInfo directoryInfo1{qmlProjectSourceId, sourceId1, qmlModuleId, Storage::Synchronization::FileType::QmlDocument}; - storage.synchronize(SynchronizationPackage{{qmlProjectSourceId}, {projectData1}}); - projectData1.moduleId = ModuleId{}; + storage.synchronize(SynchronizationPackage{{qmlProjectSourceId}, {directoryInfo1}}); + directoryInfo1.moduleId = ModuleId{}; - storage.synchronize(SynchronizationPackage{{qmlProjectSourceId}, {projectData1}}); + storage.synchronize(SynchronizationPackage{{qmlProjectSourceId}, {directoryInfo1}}); - ASSERT_THAT(storage.fetchProjectDatas({qmlProjectSourceId, qtQuickProjectSourceId}), - UnorderedElementsAre(projectData1)); + ASSERT_THAT(storage.fetchDirectoryInfos({qmlProjectSourceId, qtQuickProjectSourceId}), + UnorderedElementsAre(directoryInfo1)); } -TEST_F(ProjectStorage, throw_for_updating_with_invalid_project_source_id_in_project_data) +TEST_F(ProjectStorage, throw_for_updating_with_invalid_project_source_id_in_directory_info) { - Storage::Synchronization::ProjectData projectData1{SourceId{}, + Storage::Synchronization::DirectoryInfo directoryInfo1{SourceId{}, sourceId1, qmlModuleId, Storage::Synchronization::FileType::QmlDocument}; - ASSERT_THROW(storage.synchronize(SynchronizationPackage{{qmlProjectSourceId}, {projectData1}}), - QmlDesigner::ProjectDataHasInvalidProjectSourceId); + ASSERT_THROW(storage.synchronize(SynchronizationPackage{{qmlProjectSourceId}, {directoryInfo1}}), + QmlDesigner::DirectoryInfoHasInvalidProjectSourceId); } -TEST_F(ProjectStorage, fetch_project_datas_by_directory_source_ids) +TEST_F(ProjectStorage, fetch_directory_infos_by_directory_source_ids) { - Storage::Synchronization::ProjectData projectData1{qmlProjectSourceId, + Storage::Synchronization::DirectoryInfo directoryInfo1{qmlProjectSourceId, sourceId1, qmlModuleId, Storage::Synchronization::FileType::QmlDocument}; - Storage::Synchronization::ProjectData projectData2{qmlProjectSourceId, + Storage::Synchronization::DirectoryInfo directoryInfo2{qmlProjectSourceId, sourceId2, qmlModuleId, Storage::Synchronization::FileType::QmlDocument}; - Storage::Synchronization::ProjectData projectData3{qtQuickProjectSourceId, + Storage::Synchronization::DirectoryInfo directoryInfo3{qtQuickProjectSourceId, sourceId3, qtQuickModuleId, Storage::Synchronization::FileType::QmlTypes}; storage.synchronize(SynchronizationPackage{{qmlProjectSourceId, qtQuickProjectSourceId}, - {projectData1, projectData2, projectData3}}); + {directoryInfo1, directoryInfo2, directoryInfo3}}); - auto projectDatas = storage.fetchProjectDatas({qmlProjectSourceId, qtQuickProjectSourceId}); + auto directoryInfos = storage.fetchDirectoryInfos({qmlProjectSourceId, qtQuickProjectSourceId}); - ASSERT_THAT(projectDatas, UnorderedElementsAre(projectData1, projectData2, projectData3)); + ASSERT_THAT(directoryInfos, UnorderedElementsAre(directoryInfo1, directoryInfo2, directoryInfo3)); } -TEST_F(ProjectStorage, fetch_project_datas_by_directory_source_id) +TEST_F(ProjectStorage, fetch_directory_infos_by_directory_source_id) { - Storage::Synchronization::ProjectData projectData1{qmlProjectSourceId, + Storage::Synchronization::DirectoryInfo directoryInfo1{qmlProjectSourceId, sourceId1, qmlModuleId, Storage::Synchronization::FileType::QmlDocument}; - Storage::Synchronization::ProjectData projectData2{qmlProjectSourceId, + Storage::Synchronization::DirectoryInfo directoryInfo2{qmlProjectSourceId, sourceId2, qmlModuleId, Storage::Synchronization::FileType::QmlDocument}; - Storage::Synchronization::ProjectData projectData3{qtQuickProjectSourceId, + Storage::Synchronization::DirectoryInfo directoryInfo3{qtQuickProjectSourceId, sourceId3, qtQuickModuleId, Storage::Synchronization::FileType::QmlTypes}; storage.synchronize(SynchronizationPackage{{qmlProjectSourceId, qtQuickProjectSourceId}, - {projectData1, projectData2, projectData3}}); + {directoryInfo1, directoryInfo2, directoryInfo3}}); - auto projectData = storage.fetchProjectDatas(qmlProjectSourceId); + auto directoryInfo = storage.fetchDirectoryInfos(qmlProjectSourceId); - ASSERT_THAT(projectData, UnorderedElementsAre(projectData1, projectData2)); + ASSERT_THAT(directoryInfo, UnorderedElementsAre(directoryInfo1, directoryInfo2)); } -TEST_F(ProjectStorage, fetch_project_data_by_source_ids) +TEST_F(ProjectStorage, fetch_directory_infos_by_directory_source_id_and_file_type) { - Storage::Synchronization::ProjectData projectData1{qmlProjectSourceId, + Storage::Synchronization::DirectoryInfo directoryInfo1{ + qmlProjectSourceId, sourceId1, qmlModuleId, Storage::Synchronization::FileType::QmlDocument}; + Storage::Synchronization::DirectoryInfo directoryInfo2{ + qmlProjectSourceId, sourceId2, ModuleId{}, Storage::Synchronization::FileType::Directory}; + Storage::Synchronization::DirectoryInfo directoryInfo3{qtQuickProjectSourceId, + sourceId3, + qtQuickModuleId, + Storage::Synchronization::FileType::QmlTypes}; + Storage::Synchronization::DirectoryInfo directoryInfo4{ + qmlProjectSourceId, sourceId4, ModuleId{}, Storage::Synchronization::FileType::Directory}; + storage.synchronize( + SynchronizationPackage{{qmlProjectSourceId, qtQuickProjectSourceId}, + {directoryInfo1, directoryInfo2, directoryInfo3, directoryInfo4}}); + + auto directoryInfo = storage.fetchDirectoryInfos(qmlProjectSourceId, + Storage::Synchronization::FileType::Directory); + + ASSERT_THAT(directoryInfo, UnorderedElementsAre(directoryInfo2, directoryInfo4)); +} + +TEST_F(ProjectStorage, fetch_subdirectory_source_ids) +{ + Storage::Synchronization::DirectoryInfo directoryInfo1{ + qmlProjectSourceId, sourceId1, qmlModuleId, Storage::Synchronization::FileType::QmlDocument}; + Storage::Synchronization::DirectoryInfo directoryInfo2{ + qmlProjectSourceId, sourceId2, ModuleId{}, Storage::Synchronization::FileType::Directory}; + Storage::Synchronization::DirectoryInfo directoryInfo3{qtQuickProjectSourceId, + sourceId3, + qtQuickModuleId, + Storage::Synchronization::FileType::QmlTypes}; + Storage::Synchronization::DirectoryInfo directoryInfo4{ + qmlProjectSourceId, sourceId4, ModuleId{}, Storage::Synchronization::FileType::Directory}; + storage.synchronize( + SynchronizationPackage{{qmlProjectSourceId, qtQuickProjectSourceId}, + {directoryInfo1, directoryInfo2, directoryInfo3, directoryInfo4}}); + + auto directoryInfo = storage.fetchSubdirectorySourceIds(qmlProjectSourceId); + + ASSERT_THAT(directoryInfo, UnorderedElementsAre(sourceId2, sourceId4)); +} + +TEST_F(ProjectStorage, fetch_directory_info_by_source_ids) +{ + Storage::Synchronization::DirectoryInfo directoryInfo1{qmlProjectSourceId, sourceId1, qmlModuleId, Storage::Synchronization::FileType::QmlDocument}; - Storage::Synchronization::ProjectData projectData2{qmlProjectSourceId, + Storage::Synchronization::DirectoryInfo directoryInfo2{qmlProjectSourceId, sourceId2, qmlModuleId, Storage::Synchronization::FileType::QmlDocument}; - Storage::Synchronization::ProjectData projectData3{qtQuickProjectSourceId, + Storage::Synchronization::DirectoryInfo directoryInfo3{qtQuickProjectSourceId, sourceId3, qtQuickModuleId, Storage::Synchronization::FileType::QmlTypes}; storage.synchronize(SynchronizationPackage{{qmlProjectSourceId, qtQuickProjectSourceId}, - {projectData1, projectData2, projectData3}}); + {directoryInfo1, directoryInfo2, directoryInfo3}}); - auto projectData = storage.fetchProjectData({sourceId2}); + auto directoryInfo = storage.fetchDirectoryInfo({sourceId2}); - ASSERT_THAT(projectData, Eq(projectData2)); + ASSERT_THAT(directoryInfo, Eq(directoryInfo2)); } TEST_F(ProjectStorage, exclude_exported_types) @@ -5468,7 +5770,7 @@ TEST_F(ProjectStorage, module_exported_import_with_indirect_different_versions) TEST_F(ProjectStorage, module_exported_import_prevent_collision_if_module_is_indirectly_reexported_multiple_times) { - ModuleId qtQuick4DModuleId{storage.moduleId("QtQuick4D")}; + ModuleId qtQuick4DModuleId{storage.moduleId("QtQuick4D", ModuleKind::QmlLibrary)}; auto package{createModuleExportedImportSynchronizationPackage()}; package.imports.emplace_back(qtQuickModuleId, Storage::Version{1}, sourceId5); package.moduleExportedImports.emplace_back(qtQuick4DModuleId, @@ -5524,8 +5826,8 @@ TEST_F(ProjectStorage, TEST_F(ProjectStorage, distinguish_between_import_kinds) { - ModuleId qml1ModuleId{storage.moduleId("Qml1")}; - ModuleId qml11ModuleId{storage.moduleId("Qml11")}; + ModuleId qml1ModuleId{storage.moduleId("Qml1", ModuleKind::QmlLibrary)}; + ModuleId qml11ModuleId{storage.moduleId("Qml11", ModuleKind::QmlLibrary)}; auto package{createSimpleSynchronizationPackage()}; package.moduleDependencies.emplace_back(qmlModuleId, Storage::Version{}, sourceId1); package.moduleDependencies.emplace_back(qml1ModuleId, Storage::Version{1}, sourceId1); @@ -7192,6 +7494,46 @@ TEST_F(ProjectStorage, synchronize_document_imports_adds_import) ASSERT_TRUE(storage.importId(imports.back())); } +TEST_F(ProjectStorage, + synchronize_document_imports_removes_import_notifies_that_type_name_cannot_be_resolved) +{ + auto package{createVerySimpleSynchronizationPackage()}; + package.imports.emplace_back(qmlModuleId, Storage::Version{}, sourceId1); + package.types[0].prototype = Storage::Synchronization::ImportedType{"Object"}; + storage.synchronize(package); + + EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("Object"), sourceId1)); + + storage.synchronizeDocumentImports({}, sourceId1); +} + +TEST_F(ProjectStorage, synchronize_document_imports_removes_import_which_makes_prototype_unresolved) +{ + auto package{createVerySimpleSynchronizationPackage()}; + package.imports.emplace_back(qmlModuleId, Storage::Version{}, sourceId1); + package.types[0].prototype = Storage::Synchronization::ImportedType{"Object"}; + storage.synchronize(package); + + storage.synchronizeDocumentImports({}, sourceId1); + + ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")), + IsPrototypeId(IsUnresolvedTypeId())); +} + +TEST_F(ProjectStorage, synchronize_document_imports_adds_import_which_makes_prototype_resolved) +{ + auto package{createVerySimpleSynchronizationPackage()}; + package.types[0].prototype = Storage::Synchronization::ImportedType{"Object"}; + storage.synchronize(package); + Storage::Imports imports; + imports.emplace_back(qmlModuleId, Storage::Version{}, sourceId1); + + storage.synchronizeDocumentImports(imports, sourceId1); + + ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")), + IsPrototypeId(fetchTypeId(sourceId2, "QObject"))); +} + TEST_F(ProjectStorage, get_exported_type_names) { auto package{createSimpleSynchronizationPackage()}; @@ -7342,9 +7684,6 @@ TEST_F(ProjectStorage, do_not_synchronize_type_annotations_without_type) package.typeAnnotations = createTypeAnnotions(); package.updatedTypeAnnotationSourceIds = createUpdatedTypeAnnotionSourceIds( package.typeAnnotations); - TypeTraits traits{TypeTraitsKind::Reference}; - traits.canBeContainer = FlagIs::True; - traits.visibleInLibrary = FlagIs::True; storage.synchronize(package); @@ -7366,13 +7705,29 @@ TEST_F(ProjectStorage, synchronize_type_annotation_type_traits) ASSERT_THAT(storage.type(fetchTypeId(sourceId2, "QObject"))->traits, traits); } +TEST_F(ProjectStorage, synchronize_type_annotation_type_traits_for_prototype_heirs) +{ + auto package{createSimpleSynchronizationPackage()}; + package.typeAnnotations = createTypeAnnotions(); + package.typeAnnotations.pop_back(); + package.updatedTypeAnnotationSourceIds = createUpdatedTypeAnnotionSourceIds( + package.typeAnnotations); + TypeTraits traits{TypeTraitsKind::Reference}; + traits.canBeContainer = FlagIs::True; + traits.visibleInLibrary = FlagIs::True; + + storage.synchronize(package); + + ASSERT_THAT(storage.type(fetchTypeId(sourceId1, "QQuickItem"))->traits, traits); +} + TEST_F(ProjectStorage, synchronize_updates_type_annotation_type_traits) { auto package{createSimpleSynchronizationPackage()}; storage.synchronize(package); SynchronizationPackage annotationPackage; annotationPackage.typeAnnotations = createTypeAnnotions(); - package.updatedTypeAnnotationSourceIds = createUpdatedTypeAnnotionSourceIds( + annotationPackage.updatedTypeAnnotationSourceIds = createUpdatedTypeAnnotionSourceIds( package.typeAnnotations); TypeTraits traits{TypeTraitsKind::Reference}; traits.canBeContainer = FlagIs::True; @@ -7383,25 +7738,47 @@ TEST_F(ProjectStorage, synchronize_updates_type_annotation_type_traits) ASSERT_THAT(storage.type(fetchTypeId(sourceId2, "QObject"))->traits, traits); } +TEST_F(ProjectStorage, synchronize_updates_type_annotation_type_traits_for_prototype_heirs) +{ + auto package{createSimpleSynchronizationPackage()}; + storage.synchronize(package); + SynchronizationPackage annotationPackage; + annotationPackage.typeAnnotations = createTypeAnnotions(); + annotationPackage.typeAnnotations.pop_back(); + annotationPackage.updatedTypeAnnotationSourceIds = createUpdatedTypeAnnotionSourceIds( + package.typeAnnotations); + TypeTraits traits{TypeTraitsKind::Reference}; + traits.canBeContainer = FlagIs::True; + traits.visibleInLibrary = FlagIs::True; + + storage.synchronize(annotationPackage); + + ASSERT_THAT(storage.type(fetchTypeId(sourceId1, "QQuickItem"))->traits, traits); +} + TEST_F(ProjectStorage, synchronize_clears_annotation_type_traits_if_annotation_was_removed) +{ + +} + +TEST_F(ProjectStorage, + synchronize_clears_annotation_type_traits_if_annotation_was_removed_for_prototype_heirs) { auto package{createSimpleSynchronizationPackage()}; package.typeAnnotations = createTypeAnnotions(); package.updatedTypeAnnotationSourceIds = createUpdatedTypeAnnotionSourceIds( package.typeAnnotations); - storage.synchronize(package); package.typeAnnotations[0].traits.isStackedContainer = FlagIs::True; - TypeTraits traits{TypeTraitsKind::Reference}; - traits.canBeContainer = FlagIs::True; - traits.visibleInLibrary = FlagIs::True; - traits.isStackedContainer = FlagIs::True; + storage.synchronize(package); + package.typeAnnotations.pop_back(); storage.synchronize(package); - ASSERT_THAT(storage.type(fetchTypeId(sourceId2, "QObject"))->traits, traits); + ASSERT_THAT(storage.type(fetchTypeId(sourceId1, "QQuickItem"))->traits, + package.typeAnnotations[0].traits); } -TEST_F(ProjectStorage, synchronize_updatesannotation_type_traits) +TEST_F(ProjectStorage, synchronize_updates_annotation_type_traits) { auto package{createSimpleSynchronizationPackage()}; package.typeAnnotations = createTypeAnnotions(); @@ -7544,6 +7921,7 @@ TEST_F(ProjectStorage, synchronize_item_library_entries) storage.allItemLibraryEntries(), UnorderedElementsAre( IsItemLibraryEntry(fetchTypeId(sourceId2, "QObject"), + "Object", "Foo", "/path/icon", "Basic Items", @@ -7555,6 +7933,7 @@ TEST_F(ProjectStorage, synchronize_item_library_entries) UnorderedElementsAre("/path/templates/frame.png", "/path/templates/frame.frag")), IsItemLibraryEntry(fetchTypeId(sourceId2, "QObject"), + "Object", "Bar", "/path/icon2", "Basic Items", @@ -7564,6 +7943,7 @@ TEST_F(ProjectStorage, synchronize_item_library_entries) UnorderedElementsAre(IsItemLibraryProperty("color", "color", "#blue")), IsEmpty()), IsItemLibraryEntry(fetchTypeId(sourceId1, "QQuickItem"), + "Item", "Item", "/path/icon3", "Advanced Items", @@ -7604,6 +7984,7 @@ TEST_F(ProjectStorage, synchronize_updates_item_library_entries) ASSERT_THAT(storage.itemLibraryEntries(fetchTypeId(sourceId2, "QObject")), ElementsAre( IsItemLibraryEntry(fetchTypeId(sourceId2, "QObject"), + "Object", "Foo", "/path/icon", "Basic Items", @@ -7682,6 +8063,7 @@ TEST_F(ProjectStorage, get_all_item_library_entries) entries, UnorderedElementsAre( IsItemLibraryEntry(fetchTypeId(sourceId2, "QObject"), + "Object", "Foo", "/path/icon", "Basic Items", @@ -7693,6 +8075,7 @@ TEST_F(ProjectStorage, get_all_item_library_entries) UnorderedElementsAre("/path/templates/frame.png", "/path/templates/frame.frag")), IsItemLibraryEntry(fetchTypeId(sourceId2, "QObject"), + "Object", "Bar", "/path/icon2", "Basic Items", @@ -7702,6 +8085,7 @@ TEST_F(ProjectStorage, get_all_item_library_entries) UnorderedElementsAre(IsItemLibraryProperty("color", "color", "#blue")), IsEmpty()), IsItemLibraryEntry(fetchTypeId(sourceId1, "QQuickItem"), + "Item", "Item", "/path/icon3", "Advanced Items", @@ -7727,6 +8111,7 @@ TEST_F(ProjectStorage, get_all_item_library_entries_handles_no_entries) ASSERT_THAT(entries, UnorderedElementsAre( IsItemLibraryEntry(fetchTypeId(sourceId1, "QQuickItem"), + "Item", "Item", "/path/icon3", "Advanced Items", @@ -7753,6 +8138,7 @@ TEST_F(ProjectStorage, get_item_library_entries_by_type_id) entries, UnorderedElementsAre( IsItemLibraryEntry(fetchTypeId(sourceId2, "QObject"), + "Object", "Foo", "/path/icon", "Basic Items", @@ -7764,6 +8150,7 @@ TEST_F(ProjectStorage, get_item_library_entries_by_type_id) UnorderedElementsAre("/path/templates/frame.png", "/path/templates/frame.frag")), IsItemLibraryEntry(fetchTypeId(sourceId2, "QObject"), + "Object", "Bar", "/path/icon2", "Basic Items", @@ -7816,6 +8203,7 @@ TEST_F(ProjectStorage, get_item_library_entries_by_source_id) entries, UnorderedElementsAre( IsItemLibraryEntry(fetchTypeId(sourceId2, "QObject"), + "Object", "Foo", "/path/icon", "Basic Items", @@ -7827,6 +8215,7 @@ TEST_F(ProjectStorage, get_item_library_entries_by_source_id) UnorderedElementsAre("/path/templates/frame.png", "/path/templates/frame.frag")), IsItemLibraryEntry(fetchTypeId(sourceId2, "QObject"), + "Object", "Bar", "/path/icon2", "Basic Items", @@ -7893,4 +8282,105 @@ TEST_F(ProjectStorage, get_no_hair_ids_for_invalid_type_id) ASSERT_THAT(heirIds, IsEmpty()); } + +TEST_F(ProjectStorage, + removed_document_import_notifies_for_prototypes_that_type_name_cannot_be_resolved) +{ + auto package{createVerySimpleSynchronizationPackage()}; + package.moduleDependencies.emplace_back(qmlNativeModuleId, Storage::Version{}, sourceId1); + package.types[0].prototype = Storage::Synchronization::ImportedType{"QObject"}; + storage.synchronize(package); + package.moduleDependencies.clear(); + package.types.clear(); + package.updatedSourceIds.clear(); + package.updatedModuleDependencySourceIds = {sourceId1}; + + EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("QObject"), sourceId1)); + + storage.synchronize(package); +} + +TEST_F(ProjectStorage, + removed_document_import_notifies_for_extensions_that_type_name_cannot_be_resolved) +{ + auto package{createVerySimpleSynchronizationPackage()}; + package.moduleDependencies.emplace_back(qmlNativeModuleId, Storage::Version{}, sourceId1); + package.types[0].extension = Storage::Synchronization::ImportedType{"QObject"}; + storage.synchronize(package); + package.moduleDependencies.clear(); + package.types.clear(); + package.updatedSourceIds.clear(); + package.updatedModuleDependencySourceIds = {sourceId1}; + + EXPECT_CALL(errorNotifierMock, typeNameCannotBeResolved(Eq("QObject"), sourceId1)); + + storage.synchronize(package); +} + +TEST_F(ProjectStorage, removed_document_import_changes_prototype_to_unresolved) +{ + auto package{createVerySimpleSynchronizationPackage()}; + package.moduleDependencies.emplace_back(qmlNativeModuleId, Storage::Version{}, sourceId1); + package.types[0].prototype = Storage::Synchronization::ImportedType{"QObject"}; + storage.synchronize(package); + package.moduleDependencies.clear(); + package.types.clear(); + package.updatedSourceIds.clear(); + package.updatedModuleDependencySourceIds = {sourceId1}; + + storage.synchronize(package); + + ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")), + Field(&Storage::Synchronization::Type::prototypeId, IsUnresolvedTypeId())); +} + +TEST_F(ProjectStorage, removed_document_import_changes_extension_to_unresolved) +{ + auto package{createVerySimpleSynchronizationPackage()}; + package.moduleDependencies.emplace_back(qmlNativeModuleId, Storage::Version{}, sourceId1); + package.types[0].extension = Storage::Synchronization::ImportedType{"QObject"}; + storage.synchronize(package); + package.moduleDependencies.clear(); + package.types.clear(); + package.updatedSourceIds.clear(); + package.updatedModuleDependencySourceIds = {sourceId1}; + + storage.synchronize(package); + + ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")), + Field(&Storage::Synchronization::Type::extensionId, IsUnresolvedTypeId())); +} + +TEST_F(ProjectStorage, added_document_import_fixes_unresolved_prototype) +{ + auto package{createVerySimpleSynchronizationPackage()}; + package.types[0].prototype = Storage::Synchronization::ImportedType{"QObject"}; + storage.synchronize(package); + package.moduleDependencies.emplace_back(qmlNativeModuleId, Storage::Version{}, sourceId1); + package.types.clear(); + package.updatedSourceIds.clear(); + package.updatedModuleDependencySourceIds = {sourceId1}; + + storage.synchronize(package); + + ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")), + IsPrototypeId(fetchTypeId(sourceId2, "QObject"))); +} + +TEST_F(ProjectStorage, added_document_import_fixes_unresolved_extension) +{ + auto package{createVerySimpleSynchronizationPackage()}; + package.types[0].extension = Storage::Synchronization::ImportedType{"QObject"}; + storage.synchronize(package); + package.moduleDependencies.emplace_back(qmlNativeModuleId, Storage::Version{}, sourceId1); + package.types.clear(); + package.updatedSourceIds.clear(); + package.updatedModuleDependencySourceIds = {sourceId1}; + + storage.synchronize(package); + + ASSERT_THAT(storage.fetchTypeByTypeId(fetchTypeId(sourceId1, "QQuickItem")), + IsExtensionId(fetchTypeId(sourceId2, "QObject"))); +} + } // namespace diff --git a/tests/unit/tests/unittests/projectstorage/projectstoragepathwatcher-test.cpp b/tests/unit/tests/unittests/projectstorage/projectstoragepathwatcher-test.cpp index 26d5af8af8a..771107ece79 100644 --- a/tests/unit/tests/unittests/projectstorage/projectstoragepathwatcher-test.cpp +++ b/tests/unit/tests/unittests/projectstorage/projectstoragepathwatcher-test.cpp @@ -3,10 +3,11 @@ #include "../utils/googletest.h" -#include "../mocks/filesystemmock.h" -#include "../mocks/mockqfilesystemwatcher.h" -#include "../mocks/mocktimer.h" -#include "../mocks/projectstoragepathwatchernotifiermock.h" +#include +#include +#include +#include +#include #include #include @@ -39,21 +40,18 @@ using QmlDesigner::WatcherEntry; class ProjectStoragePathWatcher : public testing::Test { protected: - static void SetUpTestSuite() + struct StaticData { - static_database = std::make_unique(":memory:", Sqlite::JournalMode::Memory); + Sqlite::Database database{":memory:", Sqlite::JournalMode::Memory}; + ProjectStorageErrorNotifierMock errorNotifierMock; + QmlDesigner::ProjectStorage storage{database, errorNotifierMock, database.isInitialized()}; + }; - static_projectStorage = std::make_unique( - *static_database, static_database->isInitialized()); - } + static void SetUpTestSuite() { staticData = std::make_unique(); } - static void TearDownTestSuite() - { - static_projectStorage.reset(); - static_database.reset(); - } + static void TearDownTestSuite() { staticData.reset(); } - ~ProjectStoragePathWatcher() { static_projectStorage->resetForTestsOnly(); } + ~ProjectStoragePathWatcher() { storage.resetForTestsOnly(); } ProjectStoragePathWatcher() { @@ -79,10 +77,9 @@ protected: protected: NiceMock notifier; NiceMock mockFileSystem; - inline static std::unique_ptr static_database; - Sqlite::Database &database = *static_database; - inline static std::unique_ptr static_projectStorage; - QmlDesigner::ProjectStorage &storage = *static_projectStorage; + inline static std::unique_ptr staticData; + Sqlite::Database &database = staticData->database; + QmlDesigner::ProjectStorage &storage = staticData->storage; SourcePathCache pathCache{storage}; Watcher watcher{pathCache, mockFileSystem, ¬ifier}; NiceMock &mockQFileSytemWatcher = watcher.fileSystemWatcher(); diff --git a/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp b/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp index 96909857b32..e38f05349be 100644 --- a/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp +++ b/tests/unit/tests/unittests/projectstorage/projectstorageupdater-test.cpp @@ -3,11 +3,12 @@ #include "../utils/googletest.h" -#include "../mocks/filesystemmock.h" -#include "../mocks/projectstoragemock.h" -#include "../mocks/projectstoragepathwatchermock.h" -#include "../mocks/qmldocumentparsermock.h" -#include "../mocks/qmltypesparsermock.h" +#include +#include +#include +#include +#include +#include #include @@ -27,10 +28,11 @@ using QmlDesigner::SourceId; namespace Storage = QmlDesigner::Storage; using QmlDesigner::IdPaths; using QmlDesigner::Storage::Import; +using QmlDesigner::Storage::ModuleKind; +using QmlDesigner::Storage::Synchronization::DirectoryInfo; using QmlDesigner::Storage::Synchronization::FileType; using QmlDesigner::Storage::Synchronization::IsAutoVersion; using QmlDesigner::Storage::Synchronization::ModuleExportedImport; -using QmlDesigner::Storage::Synchronization::ProjectData; using QmlDesigner::Storage::Synchronization::SynchronizationPackage; using QmlDesigner::Storage::TypeTraits; using QmlDesigner::Storage::TypeTraitsKind; @@ -95,21 +97,21 @@ MATCHER_P3(IsFileStatus, && fileStatus.lastModified == lastModified; } -MATCHER_P4(IsProjectData, - projectSourceId, +MATCHER_P4(IsDirectoryInfo, + directorySourceId, sourceId, moduleId, fileType, std::string(negation ? "isn't " : "is ") - + PrintToString(Storage::Synchronization::ProjectData{ - projectSourceId, sourceId, moduleId, fileType})) + + PrintToString(Storage::Synchronization::DirectoryInfo{ + directorySourceId, sourceId, moduleId, fileType})) { - const Storage::Synchronization::ProjectData &projectData = arg; + const Storage::Synchronization::DirectoryInfo &directoryInfo = arg; - return compareInvalidAreTrue(projectData.projectSourceId, projectSourceId) - && projectData.sourceId == sourceId - && compareInvalidAreTrue(projectData.moduleId, moduleId) - && projectData.fileType == fileType; + return compareInvalidAreTrue(directoryInfo.directorySourceId, directorySourceId) + && directoryInfo.sourceId == sourceId + && compareInvalidAreTrue(directoryInfo.moduleId, moduleId) + && directoryInfo.fileType == fileType; } MATCHER(PackageIsEmpty, std::string(negation ? "isn't empty" : "is empty")) @@ -117,9 +119,10 @@ MATCHER(PackageIsEmpty, std::string(negation ? "isn't empty" : "is empty")) const Storage::Synchronization::SynchronizationPackage &package = arg; return package.imports.empty() && package.types.empty() && package.fileStatuses.empty() - && package.updatedSourceIds.empty() && package.projectDatas.empty() - && package.updatedFileStatusSourceIds.empty() && package.updatedProjectSourceIds.empty() - && package.moduleDependencies.empty() && package.updatedModuleDependencySourceIds.empty() + && package.updatedSourceIds.empty() && package.directoryInfos.empty() + && package.updatedFileStatusSourceIds.empty() + && package.updatedDirectoryInfoSourceIds.empty() && package.moduleDependencies.empty() + && package.updatedModuleDependencySourceIds.empty() && package.moduleExportedImports.empty() && package.updatedModuleIds.empty() && package.propertyEditorQmlPaths.empty() && package.updatedPropertyEditorQmlPathSourceIds.empty() @@ -140,19 +143,16 @@ auto IsPropertyEditorQmlPath(const ModuleIdMatcher &moduleIdMatcher, class ProjectStorageUpdater : public testing::Test { public: - static void SetUpTestSuite() + struct StaticData { - static_database = std::make_unique(":memory:", Sqlite::JournalMode::Memory); + Sqlite::Database database{":memory:", Sqlite::JournalMode::Memory}; + NiceMock errorNotifierMock; + QmlDesigner::ProjectStorage storage{database, errorNotifierMock, database.isInitialized()}; + }; - static_projectStorage = std::make_unique( - *static_database, static_database->isInitialized()); - } + static void SetUpTestSuite() { staticData = std::make_unique(); } - static void TearDownTestSuite() - { - static_projectStorage.reset(); - static_database.reset(); - } + static void TearDownTestSuite() { staticData.reset(); } ProjectStorageUpdater() { @@ -180,8 +180,8 @@ public: setQmlFileNames(u"/path", {"First.qml", "First2.qml", "Second.qml"}); - ON_CALL(projectStorageMock, moduleId(_)).WillByDefault([&](const auto &name) { - return storage.moduleId(name); + ON_CALL(projectStorageMock, moduleId(_, _)).WillByDefault([&](const auto &name, const auto &kind) { + return storage.moduleId(name, kind); }); firstType.prototype = Storage::Synchronization::ImportedType{"Object"}; @@ -226,7 +226,7 @@ public: }); } - ~ProjectStorageUpdater() { static_projectStorage->resetForTestsOnly(); } + ~ProjectStorageUpdater() { storage.resetForTestsOnly(); } void setFilesDontChanged(const QmlDesigner::SourceIds &sourceIds) { @@ -281,14 +281,14 @@ public: ON_CALL(fileSystemMock, qmlFileNames(Eq(directoryPath))).WillByDefault(Return(qmlFileNames)); } - void setProjectDatas(SourceId directoryPathSourceId, - const QmlDesigner::Storage::Synchronization::ProjectDatas &projectDatas) + void setDirectoryInfos(SourceId directoryPathSourceId, + const QmlDesigner::Storage::Synchronization::DirectoryInfos &directoryInfos) { - ON_CALL(projectStorageMock, fetchProjectDatas(Eq(directoryPathSourceId))) - .WillByDefault(Return(projectDatas)); - for (const ProjectData &projectData : projectDatas) { - ON_CALL(projectStorageMock, fetchProjectData(Eq(projectData.sourceId))) - .WillByDefault(Return(std::optional{projectData})); + ON_CALL(projectStorageMock, fetchDirectoryInfos(Eq(directoryPathSourceId))) + .WillByDefault(Return(directoryInfos)); + for (const DirectoryInfo &directoryInfo : directoryInfos) { + ON_CALL(projectStorageMock, fetchDirectoryInfo(Eq(directoryInfo.sourceId))) + .WillByDefault(Return(std::optional{directoryInfo})); } } @@ -302,7 +302,22 @@ public: EXPECT_CALL(fileSystemMock, contentAsQString(Eq(path))).WillRepeatedly(Return(content)); } - auto moduleId(Utils::SmallStringView name) const { return storage.moduleId(name); } + void setSubdirectoryPaths(QStringView directoryPath, const QStringList &subdirectoryPaths) + { + ON_CALL(fileSystemMock, subdirectories(Eq(directoryPath))).WillByDefault(Return(subdirectoryPaths)); + } + + void setSubdirectorySourceIds(SourceId directorySourceId, + const QmlDesigner::SmallSourceIds<32> &subdirectorySourceId) + { + ON_CALL(projectStorageMock, fetchSubdirectorySourceIds(Eq(directorySourceId))) + .WillByDefault(Return(subdirectorySourceId)); + } + + auto moduleId(Utils::SmallStringView name, ModuleKind kind) const + { + return storage.moduleId(name, kind); + } protected: NiceMock fileSystemMock; @@ -310,10 +325,9 @@ protected: NiceMock qmlTypesParserMock; NiceMock qmlDocumentParserMock; QmlDesigner::FileStatusCache fileStatusCache{fileSystemMock}; - inline static std::unique_ptr static_database; - Sqlite::Database &database = *static_database; - inline static std::unique_ptr static_projectStorage; - QmlDesigner::ProjectStorage &storage = *static_projectStorage; + inline static std::unique_ptr staticData; + Sqlite::Database &database = staticData->database; + QmlDesigner::ProjectStorage &storage = staticData->storage; QmlDesigner::SourcePathCache sourcePathCache{ storage}; NiceMock patchWatcherMock; @@ -341,16 +355,16 @@ protected: QmlDesigner::SourcePath{itemLibraryPath + "/."}); SourceId qmlImportsPathSourceId = sourcePathCache.sourceId( QmlDesigner::SourcePath{qmlImportsPath + "/."}); - ModuleId qmlModuleId{storage.moduleId("Qml")}; - ModuleId qmlCppNativeModuleId{storage.moduleId("Qml-cppnative")}; - ModuleId exampleModuleId{storage.moduleId("Example")}; - ModuleId exampleCppNativeModuleId{storage.moduleId("Example-cppnative")}; - ModuleId builtinModuleId{storage.moduleId("QML")}; - ModuleId builtinCppNativeModuleId{storage.moduleId("QML-cppnative")}; - ModuleId quickModuleId{storage.moduleId("Quick")}; - ModuleId quickCppNativeModuleId{storage.moduleId("Quick-cppnative")}; - ModuleId pathModuleId{storage.moduleId("/path")}; - ModuleId subPathQmlModuleId{storage.moduleId("/path/qml")}; + ModuleId qmlModuleId{storage.moduleId("Qml", ModuleKind::QmlLibrary)}; + ModuleId qmlCppNativeModuleId{storage.moduleId("Qml", ModuleKind::CppLibrary)}; + ModuleId exampleModuleId{storage.moduleId("Example", ModuleKind::QmlLibrary)}; + ModuleId exampleCppNativeModuleId{storage.moduleId("Example", ModuleKind::CppLibrary)}; + ModuleId builtinModuleId{storage.moduleId("QML", ModuleKind::QmlLibrary)}; + ModuleId builtinCppNativeModuleId{storage.moduleId("QML", ModuleKind::CppLibrary)}; + ModuleId quickModuleId{storage.moduleId("Quick", ModuleKind::QmlLibrary)}; + ModuleId quickCppNativeModuleId{storage.moduleId("Quick", ModuleKind::CppLibrary)}; + ModuleId pathModuleId{storage.moduleId("/path", ModuleKind::PathLibrary)}; + ModuleId subPathQmlModuleId{storage.moduleId("/path/qml", ModuleKind::PathLibrary)}; Storage::Synchronization::Type objectType{ "QObject", Storage::Synchronization::ImportedType{}, @@ -426,6 +440,24 @@ TEST_F(ProjectStorageUpdater, get_content_for_qml_dir_paths_if_file_status_is_di updater.update(directories, {}, {}, {}); } +TEST_F(ProjectStorageUpdater, + get_content_for_qml_dir_paths_if_file_status_is_different_for_subdirectories) +{ + SourceId qmlDir1PathSourceId = sourcePathCache.sourceId("/path/one/qmldir"); + SourceId qmlDir2PathSourceId = sourcePathCache.sourceId("/path/two/qmldir"); + SourceId qmlDir3PathSourceId = sourcePathCache.sourceId("/path/three/qmldir"); + SourceId path3SourceId = sourcePathCache.sourceId("/path/three/."); + QStringList directories = {"/path/one"}; + setSubdirectoryPaths(u"/path/one", {"/path/two", "/path/three"}); + setFilesChanged({qmlDir1PathSourceId, qmlDir2PathSourceId}); + setFilesDontChanged({qmlDir3PathSourceId, path3SourceId}); + + EXPECT_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/one/qmldir")))); + EXPECT_CALL(fileSystemMock, contentAsQString(Eq(QString("/path/two/qmldir")))); + + updater.update(directories, {}, {}, {}); +} + TEST_F(ProjectStorageUpdater, request_file_status_from_file_system) { EXPECT_CALL(fileSystemMock, fileStatus(Ne(directoryPathSourceId))).Times(AnyNumber()); @@ -435,6 +467,20 @@ TEST_F(ProjectStorageUpdater, request_file_status_from_file_system) updater.update(directories, {}, {}, {}); } +TEST_F(ProjectStorageUpdater, request_file_status_from_file_system_for_subdirectories) +{ + EXPECT_CALL(fileSystemMock, + fileStatus(AllOf(Ne(directoryPathSourceId), Ne(path1SourceId), Ne(path2SourceId)))) + .Times(AnyNumber()); + setSubdirectoryPaths(u"/path", {"/path/one", "/path/two"}); + + EXPECT_CALL(fileSystemMock, fileStatus(Eq(path1SourceId))); + EXPECT_CALL(fileSystemMock, fileStatus(Eq(path2SourceId))); + EXPECT_CALL(fileSystemMock, fileStatus(Eq(directoryPathSourceId))); + + updater.update(directories, {}, {}, {}); +} + TEST_F(ProjectStorageUpdater, get_content_for_qml_types) { QString qmldir{R"(module Example @@ -472,9 +518,30 @@ TEST_F(ProjectStorageUpdater, parse_qml_types) setContent(u"/path/example2.qmltypes", qmltypes2); EXPECT_CALL(qmlTypesParserMock, - parse(qmltypes, _, _, Field(&ProjectData::moduleId, exampleCppNativeModuleId))); + parse(qmltypes, _, _, Field(&DirectoryInfo::moduleId, exampleCppNativeModuleId))); EXPECT_CALL(qmlTypesParserMock, - parse(qmltypes2, _, _, Field(&ProjectData::moduleId, exampleCppNativeModuleId))); + parse(qmltypes2, _, _, Field(&DirectoryInfo::moduleId, exampleCppNativeModuleId))); + + updater.update(directories, {}, {}, {}); +} + +TEST_F(ProjectStorageUpdater, parse_qml_types_in_subdirectories) +{ + QString qmldir{R"(module Example + typeinfo example.qmltypes + typeinfo example2.qmltypes)"}; + setContent(u"/path/qmldir", qmldir); + QString qmltypes{"Module {\ndependencies: []}"}; + QString qmltypes2{"Module {\ndependencies: [foo]}"}; + setContent(u"/path/example.qmltypes", qmltypes); + setContent(u"/path/example2.qmltypes", qmltypes2); + QStringList directories = {"/root"}; + setSubdirectoryPaths(u"/root", {"/path"}); + + EXPECT_CALL(qmlTypesParserMock, + parse(qmltypes, _, _, Field(&DirectoryInfo::moduleId, exampleCppNativeModuleId))); + EXPECT_CALL(qmlTypesParserMock, + parse(qmltypes2, _, _, Field(&DirectoryInfo::moduleId, exampleCppNativeModuleId))); updater.update(directories, {}, {}, {}); } @@ -488,6 +555,23 @@ TEST_F(ProjectStorageUpdater, synchronize_is_empty_for_no_change) updater.update(directories, {}, {}, {}); } +TEST_F(ProjectStorageUpdater, synchronize_is_empty_for_no_change_in_subdirectory) +{ + SourceId qmlDirRootPathSourceId = sourcePathCache.sourceId("/root/qmldir"); + SourceId rootPathSourceId = sourcePathCache.sourceId("/root/."); + setFilesDontChanged({qmltypesPathSourceId, + qmltypes2PathSourceId, + qmlDirPathSourceId, + qmlDirRootPathSourceId, + rootPathSourceId}); + QStringList directories = {"/root"}; + setSubdirectoryPaths(u"/root", {"/path"}); + + EXPECT_CALL(projectStorageMock, synchronize(PackageIsEmpty())); + + updater.update(directories, {}, {}, {}); +} + TEST_F(ProjectStorageUpdater, synchronize_qml_types) { Storage::Import import{qmlModuleId, Storage::Version{2, 3}, qmltypesPathSourceId}; @@ -500,9 +584,9 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_types) imports.push_back(import); }); - EXPECT_CALL(projectStorageMock, moduleId(Eq("Example"))); - EXPECT_CALL(projectStorageMock, moduleId(Eq("Example-cppnative"))); - EXPECT_CALL(projectStorageMock, moduleId(Eq("/path"))); + EXPECT_CALL(projectStorageMock, moduleId(Eq("Example"), ModuleKind::QmlLibrary)); + EXPECT_CALL(projectStorageMock, moduleId(Eq("Example"), ModuleKind::CppLibrary)); + EXPECT_CALL(projectStorageMock, moduleId(Eq("/path"), ModuleKind::PathLibrary)); EXPECT_CALL(projectStorageMock, synchronize( AllOf(Field(&SynchronizationPackage::imports, ElementsAre(import)), @@ -512,17 +596,88 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_types) Field(&SynchronizationPackage::fileStatuses, UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 1, 21), IsFileStatus(qmltypesPathSourceId, 1, 21))), - Field(&SynchronizationPackage::projectDatas, - UnorderedElementsAre(IsProjectData(directoryPathSourceId, - qmltypesPathSourceId, - exampleCppNativeModuleId, - FileType::QmlTypes))), - Field(&SynchronizationPackage::updatedProjectSourceIds, + Field(&SynchronizationPackage::directoryInfos, + UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, + qmltypesPathSourceId, + exampleCppNativeModuleId, + FileType::QmlTypes))), + Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds, UnorderedElementsAre(directoryPathSourceId))))); updater.update(directories, {}, {}, {}); } +TEST_F(ProjectStorageUpdater, synchronize_subdircectories) +{ + QStringList directories = {"/root"}; + setSubdirectoryPaths(u"/root", {"/path/one", "/path/two"}); + setSubdirectoryPaths(u"/path/one", {"/path/three"}); + SourceId rootDirectoryPathSourceId = sourcePathCache.sourceId("/root/."); + setFilesChanged({rootDirectoryPathSourceId, path1SourceId, path2SourceId, path3SourceId}); + + EXPECT_CALL( + projectStorageMock, + synchronize(AllOf( + Field(&SynchronizationPackage::directoryInfos, + UnorderedElementsAre( + IsDirectoryInfo(rootDirectoryPathSourceId, path1SourceId, ModuleId{}, FileType::Directory), + IsDirectoryInfo(rootDirectoryPathSourceId, path2SourceId, ModuleId{}, FileType::Directory), + IsDirectoryInfo(path1SourceId, path3SourceId, ModuleId{}, FileType::Directory))), + Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds, + UnorderedElementsAre( + rootDirectoryPathSourceId, path1SourceId, path2SourceId, path3SourceId))))); + + updater.update(directories, {}, {}, {}); +} + +TEST_F(ProjectStorageUpdater, synchronize_subdircectories_even_for_no_changes) +{ + QStringList directories = {"/root"}; + setSubdirectoryPaths(u"/root", {"/path/one", "/path/two"}); + setSubdirectoryPaths(u"/path/one", {"/path/three"}); + SourceId rootDirectoryPathSourceId = sourcePathCache.sourceId("/root/."); + setFilesChanged({path1SourceId, path2SourceId, path3SourceId}); + setFilesDontChanged({rootDirectoryPathSourceId}); + + EXPECT_CALL(projectStorageMock, + synchronize( + AllOf(Field(&SynchronizationPackage::directoryInfos, + UnorderedElementsAre(IsDirectoryInfo( + path1SourceId, path3SourceId, ModuleId{}, FileType::Directory))), + Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds, + UnorderedElementsAre(path1SourceId, path2SourceId, path3SourceId))))); + + updater.update(directories, {}, {}, {}); +} + +TEST_F(ProjectStorageUpdater, synchronize_subdircectories_for_deleted_subdirecties) +{ + QStringList directories = {"/root"}; + setSubdirectoryPaths(u"/root", {"/path/two"}); + SourceId rootDirectoryPathSourceId = sourcePathCache.sourceId("/root/."); + setFilesChanged({rootDirectoryPathSourceId}); + setFilesDontExists({ + path1SourceId, + path3SourceId, + }); + setSubdirectorySourceIds(rootDirectoryPathSourceId, {path1SourceId, path2SourceId}); + setSubdirectorySourceIds(path1SourceId, {path3SourceId}); + + EXPECT_CALL(projectStorageMock, + synchronize(AllOf(Field(&SynchronizationPackage::directoryInfos, + UnorderedElementsAre(IsDirectoryInfo(rootDirectoryPathSourceId, + path2SourceId, + ModuleId{}, + FileType::Directory))), + Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds, + UnorderedElementsAre(rootDirectoryPathSourceId, + path1SourceId, + path2SourceId, + path3SourceId))))); + + updater.update(directories, {}, {}, {}); +} + TEST_F(ProjectStorageUpdater, synchronize_qml_types_throws_if_qmltpes_does_not_exists) { Storage::Import import{qmlModuleId, Storage::Version{2, 3}, qmltypesPathSourceId}; @@ -649,21 +804,21 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents) IsFileStatus(qmlDocumentSourceId1, 1, 21), IsFileStatus(qmlDocumentSourceId2, 1, 21), IsFileStatus(qmlDocumentSourceId3, 1, 21))), - Field(&SynchronizationPackage::updatedProjectSourceIds, + Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds, UnorderedElementsAre(directoryPathSourceId)), - Field(&SynchronizationPackage::projectDatas, - UnorderedElementsAre(IsProjectData(directoryPathSourceId, - qmlDocumentSourceId1, - ModuleId{}, - FileType::QmlDocument), - IsProjectData(directoryPathSourceId, - qmlDocumentSourceId2, - ModuleId{}, - FileType::QmlDocument), - IsProjectData(directoryPathSourceId, - qmlDocumentSourceId3, - ModuleId{}, - FileType::QmlDocument)))))); + Field(&SynchronizationPackage::directoryInfos, + UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId1, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId2, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId3, + ModuleId{}, + FileType::QmlDocument)))))); updater.update(directories, {}, {}, {}); } @@ -676,8 +831,8 @@ TEST_F(ProjectStorageUpdater, synchronize_add_only_qml_document_in_directory) setFilesChanged({directoryPathSourceId}); setFilesDontChanged({qmlDirPathSourceId, qmlDocumentSourceId1}); setFilesAdded({qmlDocumentSourceId2}); - setProjectDatas(directoryPathSourceId, - {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument}}); + setDirectoryInfos(directoryPathSourceId, + {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument}}); setQmlFileNames(u"/path", {"First.qml", "First2.qml"}); EXPECT_CALL(projectStorageMock, @@ -709,17 +864,17 @@ TEST_F(ProjectStorageUpdater, synchronize_add_only_qml_document_in_directory) Field(&SynchronizationPackage::fileStatuses, UnorderedElementsAre(IsFileStatus(qmlDocumentSourceId2, 1, 21), IsFileStatus(directoryPathSourceId, 1, 21))), - Field(&SynchronizationPackage::updatedProjectSourceIds, + Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds, UnorderedElementsAre(directoryPathSourceId)), - Field(&SynchronizationPackage::projectDatas, - UnorderedElementsAre(IsProjectData(directoryPathSourceId, - qmlDocumentSourceId1, - ModuleId{}, - FileType::QmlDocument), - IsProjectData(directoryPathSourceId, - qmlDocumentSourceId2, - ModuleId{}, - FileType::QmlDocument)))))); + Field(&SynchronizationPackage::directoryInfos, + UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId1, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId2, + ModuleId{}, + FileType::QmlDocument)))))); updater.update(directories, {}, {}, {}); } @@ -734,10 +889,11 @@ TEST_F(ProjectStorageUpdater, synchronize_removes_qml_document) setFilesChanged({qmlDirPathSourceId}); setFilesDontChanged({qmlDocumentSourceId1, qmlDocumentSourceId2}); setFilesRemoved({qmlDocumentSourceId3}); - setProjectDatas(directoryPathSourceId, - {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument}, - {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument}, - {directoryPathSourceId, qmlDocumentSourceId3, ModuleId{}, FileType::QmlDocument}}); + setDirectoryInfos( + directoryPathSourceId, + {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument}, + {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument}, + {directoryPathSourceId, qmlDocumentSourceId3, ModuleId{}, FileType::QmlDocument}}); setQmlFileNames(u"/path", {"First.qml", "First2.qml"}); EXPECT_CALL(projectStorageMock, @@ -772,17 +928,17 @@ TEST_F(ProjectStorageUpdater, synchronize_removes_qml_document) UnorderedElementsAre(qmlDirPathSourceId, qmlDocumentSourceId3)), Field(&SynchronizationPackage::fileStatuses, UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 1, 21))), - Field(&SynchronizationPackage::updatedProjectSourceIds, + Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds, UnorderedElementsAre(directoryPathSourceId)), - Field(&SynchronizationPackage::projectDatas, - UnorderedElementsAre(IsProjectData(directoryPathSourceId, - qmlDocumentSourceId1, - ModuleId{}, - FileType::QmlDocument), - IsProjectData(directoryPathSourceId, - qmlDocumentSourceId2, - ModuleId{}, - FileType::QmlDocument)))))); + Field(&SynchronizationPackage::directoryInfos, + UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId1, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId2, + ModuleId{}, + FileType::QmlDocument)))))); updater.update(directories, {}, {}, {}); } @@ -795,9 +951,9 @@ TEST_F(ProjectStorageUpdater, synchronize_removes_qml_document_in_qmldir_only) setContent(u"/path/qmldir", qmldir); setFilesChanged({qmlDirPathSourceId}); setFilesDontChanged({qmlDocumentSourceId1, qmlDocumentSourceId2}); - setProjectDatas(directoryPathSourceId, - {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument}, - {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument}}); + setDirectoryInfos(directoryPathSourceId, + {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument}, + {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument}}); setQmlFileNames(u"/path", {"First.qml", "First2.qml"}); EXPECT_CALL( @@ -827,17 +983,17 @@ TEST_F(ProjectStorageUpdater, synchronize_removes_qml_document_in_qmldir_only) UnorderedElementsAre(qmlDirPathSourceId)), Field(&SynchronizationPackage::fileStatuses, UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 1, 21))), - Field(&SynchronizationPackage::updatedProjectSourceIds, + Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds, UnorderedElementsAre(directoryPathSourceId)), - Field(&SynchronizationPackage::projectDatas, - UnorderedElementsAre(IsProjectData(directoryPathSourceId, - qmlDocumentSourceId1, - ModuleId{}, - FileType::QmlDocument), - IsProjectData(directoryPathSourceId, - qmlDocumentSourceId2, - ModuleId{}, - FileType::QmlDocument)))))); + Field(&SynchronizationPackage::directoryInfos, + UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId1, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId2, + ModuleId{}, + FileType::QmlDocument)))))); updater.update(directories, {}, {}, {}); } @@ -851,9 +1007,9 @@ TEST_F(ProjectStorageUpdater, synchronize_add_qml_document_to_qmldir) setContent(u"/path/qmldir", qmldir); setFilesChanged({qmlDirPathSourceId}); setFilesDontChanged({qmlDocumentSourceId1, qmlDocumentSourceId2}); - setProjectDatas(directoryPathSourceId, - {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument}, - {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument}}); + setDirectoryInfos(directoryPathSourceId, + {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument}, + {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument}}); setQmlFileNames(u"/path", {"First.qml", "First2.qml"}); EXPECT_CALL( @@ -885,17 +1041,17 @@ TEST_F(ProjectStorageUpdater, synchronize_add_qml_document_to_qmldir) UnorderedElementsAre(qmlDirPathSourceId)), Field(&SynchronizationPackage::fileStatuses, UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 1, 21))), - Field(&SynchronizationPackage::updatedProjectSourceIds, + Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds, UnorderedElementsAre(directoryPathSourceId)), - Field(&SynchronizationPackage::projectDatas, - UnorderedElementsAre(IsProjectData(directoryPathSourceId, - qmlDocumentSourceId1, - ModuleId{}, - FileType::QmlDocument), - IsProjectData(directoryPathSourceId, - qmlDocumentSourceId2, - ModuleId{}, - FileType::QmlDocument)))))); + Field(&SynchronizationPackage::directoryInfos, + UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId1, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId2, + ModuleId{}, + FileType::QmlDocument)))))); updater.update(directories, {}, {}, {}); } @@ -908,9 +1064,9 @@ TEST_F(ProjectStorageUpdater, synchronize_remove_qml_document_from_qmldir) setContent(u"/path/qmldir", qmldir); setFilesDontChanged({qmlDocumentSourceId1, qmlDocumentSourceId2}); setFilesChanged({qmlDirPathSourceId}); - setProjectDatas(directoryPathSourceId, - {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument}, - {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument}}); + setDirectoryInfos(directoryPathSourceId, + {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument}, + {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument}}); setQmlFileNames(u"/path", {"First.qml", "First2.qml"}); EXPECT_CALL( @@ -940,17 +1096,17 @@ TEST_F(ProjectStorageUpdater, synchronize_remove_qml_document_from_qmldir) UnorderedElementsAre(qmlDirPathSourceId)), Field(&SynchronizationPackage::fileStatuses, UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 1, 21))), - Field(&SynchronizationPackage::updatedProjectSourceIds, + Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds, UnorderedElementsAre(directoryPathSourceId)), - Field(&SynchronizationPackage::projectDatas, - UnorderedElementsAre(IsProjectData(directoryPathSourceId, - qmlDocumentSourceId1, - ModuleId{}, - FileType::QmlDocument), - IsProjectData(directoryPathSourceId, - qmlDocumentSourceId2, - ModuleId{}, - FileType::QmlDocument)))))); + Field(&SynchronizationPackage::directoryInfos, + UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId1, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId2, + ModuleId{}, + FileType::QmlDocument)))))); updater.update(directories, {}, {}, {}); } @@ -1006,28 +1162,28 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents_dont_update_if_up_to_dat IsFileStatus(qmlDocumentSourceId2, 1, 21))), Field(&SynchronizationPackage::updatedFileStatusSourceIds, UnorderedElementsAre(qmlDirPathSourceId, qmlDocumentSourceId1, qmlDocumentSourceId2)), - Field(&SynchronizationPackage::updatedProjectSourceIds, + Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds, UnorderedElementsAre(directoryPathSourceId)), - Field(&SynchronizationPackage::projectDatas, - UnorderedElementsAre(IsProjectData(directoryPathSourceId, - qmlDocumentSourceId1, - ModuleId{}, - FileType::QmlDocument), - IsProjectData(directoryPathSourceId, - qmlDocumentSourceId2, - ModuleId{}, - FileType::QmlDocument), - IsProjectData(directoryPathSourceId, - qmlDocumentSourceId3, - ModuleId{}, - FileType::QmlDocument)))))); + Field(&SynchronizationPackage::directoryInfos, + UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId1, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId2, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId3, + ModuleId{}, + FileType::QmlDocument)))))); updater.update(directories, {}, {}, {}); } TEST_F(ProjectStorageUpdater, synchroniz_if_qmldir_file_has_not_changed) { - setProjectDatas( + setDirectoryInfos( directoryPathSourceId, {{directoryPathSourceId, qmltypesPathSourceId, exampleModuleId, FileType::QmlTypes}, {directoryPathSourceId, qmltypes2PathSourceId, exampleModuleId, FileType::QmlTypes}, @@ -1071,14 +1227,14 @@ TEST_F(ProjectStorageUpdater, synchroniz_if_qmldir_file_has_not_changed) qmltypes2PathSourceId, qmlDocumentSourceId1, qmlDocumentSourceId2)), - Field(&SynchronizationPackage::projectDatas, IsEmpty())))); + Field(&SynchronizationPackage::directoryInfos, IsEmpty())))); updater.update(directories, {}, {}, {}); } TEST_F(ProjectStorageUpdater, synchroniz_if_qmldir_file_has_not_changed_and_some_updated_files) { - setProjectDatas( + setDirectoryInfos( directoryPathSourceId, {{directoryPathSourceId, qmltypesPathSourceId, exampleModuleId, FileType::QmlTypes}, {directoryPathSourceId, qmltypes2PathSourceId, exampleModuleId, FileType::QmlTypes}, @@ -1106,7 +1262,7 @@ TEST_F(ProjectStorageUpdater, synchroniz_if_qmldir_file_has_not_changed_and_some IsFileStatus(qmlDocumentSourceId1, 1, 21))), Field(&SynchronizationPackage::updatedFileStatusSourceIds, UnorderedElementsAre(qmltypesPathSourceId, qmlDocumentSourceId1)), - Field(&SynchronizationPackage::projectDatas, IsEmpty())))); + Field(&SynchronizationPackage::directoryInfos, IsEmpty())))); updater.update(directories, {}, {}, {}); } @@ -1114,7 +1270,7 @@ TEST_F(ProjectStorageUpdater, synchroniz_if_qmldir_file_has_not_changed_and_some TEST_F(ProjectStorageUpdater, synchroniz_if_qmldir_file_not_changed_and_some_removed_files) { setQmlFileNames(u"/path", {"First2.qml"}); - setProjectDatas( + setDirectoryInfos( directoryPathSourceId, {{directoryPathSourceId, qmltypesPathSourceId, exampleModuleId, FileType::QmlTypes}, {directoryPathSourceId, qmltypes2PathSourceId, exampleModuleId, FileType::QmlTypes}, @@ -1133,7 +1289,7 @@ TEST_F(ProjectStorageUpdater, synchroniz_if_qmldir_file_has_changed_and_some_rem typeinfo example2.qmltypes)"}; setContent(u"/path/qmldir", qmldir); setQmlFileNames(u"/path", {"First2.qml"}); - setProjectDatas( + setDirectoryInfos( directoryPathSourceId, {{directoryPathSourceId, qmltypesPathSourceId, exampleModuleId, FileType::QmlTypes}, {directoryPathSourceId, qmltypes2PathSourceId, exampleModuleId, FileType::QmlTypes}, @@ -1165,15 +1321,15 @@ TEST_F(ProjectStorageUpdater, synchroniz_if_qmldir_file_has_changed_and_some_rem UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 1, 21))), Field(&SynchronizationPackage::updatedFileStatusSourceIds, UnorderedElementsAre(qmlDirPathSourceId, qmltypesPathSourceId, qmlDocumentSourceId1)), - Field(&SynchronizationPackage::projectDatas, - UnorderedElementsAre(IsProjectData(directoryPathSourceId, - qmlDocumentSourceId2, - ModuleId{}, - FileType::QmlDocument), - IsProjectData(directoryPathSourceId, - qmltypes2PathSourceId, - exampleCppNativeModuleId, - FileType::QmlTypes)))))); + Field(&SynchronizationPackage::directoryInfos, + UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId2, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmltypes2PathSourceId, + exampleCppNativeModuleId, + FileType::QmlTypes)))))); updater.update(directories, {}, {}, {}); } @@ -1187,8 +1343,8 @@ TEST_F(ProjectStorageUpdater, update_qml_types_files_is_empty) Field(&SynchronizationPackage::updatedSourceIds, IsEmpty()), Field(&SynchronizationPackage::fileStatuses, IsEmpty()), Field(&SynchronizationPackage::updatedFileStatusSourceIds, IsEmpty()), - Field(&SynchronizationPackage::projectDatas, IsEmpty()), - Field(&SynchronizationPackage::updatedProjectSourceIds, IsEmpty())))); + Field(&SynchronizationPackage::directoryInfos, IsEmpty()), + Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds, IsEmpty())))); updater.update({}, {}, {}, {}); } @@ -1206,16 +1362,16 @@ TEST_F(ProjectStorageUpdater, update_qml_types_files) IsFileStatus(qmltypes2PathSourceId, 1, 21))), Field(&SynchronizationPackage::updatedFileStatusSourceIds, UnorderedElementsAre(qmltypesPathSourceId, qmltypes2PathSourceId)), - Field(&SynchronizationPackage::projectDatas, - UnorderedElementsAre(IsProjectData(qmltypesPathSourceId, - qmltypesPathSourceId, - builtinCppNativeModuleId, - FileType::QmlTypes), - IsProjectData(qmltypes2PathSourceId, - qmltypes2PathSourceId, - builtinCppNativeModuleId, - FileType::QmlTypes))), - Field(&SynchronizationPackage::updatedProjectSourceIds, + Field(&SynchronizationPackage::directoryInfos, + UnorderedElementsAre(IsDirectoryInfo(qmltypesPathSourceId, + qmltypesPathSourceId, + builtinCppNativeModuleId, + FileType::QmlTypes), + IsDirectoryInfo(qmltypes2PathSourceId, + qmltypes2PathSourceId, + builtinCppNativeModuleId, + FileType::QmlTypes))), + Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds, UnorderedElementsAre(qmltypesPathSourceId, qmltypes2PathSourceId))))); updater.update({}, {"/path/example.qmltypes", "/path/example2.qmltypes"}, {}, {}); @@ -1235,12 +1391,12 @@ TEST_F(ProjectStorageUpdater, dont_update_qml_types_files_if_unchanged) UnorderedElementsAre(IsFileStatus(qmltypesPathSourceId, 1, 21))), Field(&SynchronizationPackage::updatedFileStatusSourceIds, UnorderedElementsAre(qmltypesPathSourceId)), - Field(&SynchronizationPackage::projectDatas, - UnorderedElementsAre(IsProjectData(qmltypesPathSourceId, - qmltypesPathSourceId, - builtinCppNativeModuleId, - FileType::QmlTypes))), - Field(&SynchronizationPackage::updatedProjectSourceIds, + Field(&SynchronizationPackage::directoryInfos, + UnorderedElementsAre(IsDirectoryInfo(qmltypesPathSourceId, + qmltypesPathSourceId, + builtinCppNativeModuleId, + FileType::QmlTypes))), + Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds, UnorderedElementsAre(qmltypesPathSourceId))))); updater.update({}, {"/path/example.qmltypes", "/path/example2.qmltypes"}, {}, {}); @@ -1278,13 +1434,13 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents_with_different_version_b Field(&SynchronizationPackage::fileStatuses, UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 1, 21), IsFileStatus(qmlDocumentSourceId1, 1, 21))), - Field(&SynchronizationPackage::updatedProjectSourceIds, + Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds, UnorderedElementsAre(directoryPathSourceId)), - Field(&SynchronizationPackage::projectDatas, - UnorderedElementsAre(IsProjectData(directoryPathSourceId, - qmlDocumentSourceId1, - ModuleId{}, - FileType::QmlDocument)))))); + Field(&SynchronizationPackage::directoryInfos, + UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId1, + ModuleId{}, + FileType::QmlDocument)))))); updater.update(directories, {}, {}, {}); } @@ -1319,13 +1475,13 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents_with_different_type_name Field(&SynchronizationPackage::fileStatuses, UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 1, 21), IsFileStatus(qmlDocumentSourceId1, 1, 21))), - Field(&SynchronizationPackage::updatedProjectSourceIds, + Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds, UnorderedElementsAre(directoryPathSourceId)), - Field(&SynchronizationPackage::projectDatas, - UnorderedElementsAre(IsProjectData(directoryPathSourceId, - qmlDocumentSourceId1, - ModuleId{}, - FileType::QmlDocument)))))); + Field(&SynchronizationPackage::directoryInfos, + UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId1, + ModuleId{}, + FileType::QmlDocument)))))); updater.update(directories, {}, {}, {}); } @@ -1541,12 +1697,12 @@ TEST_F(ProjectStorageUpdater, synchronize_qmldir_imports_with_double_entries) updater.update(directories, {}, {}, {}); } -TEST_F(ProjectStorageUpdater, synchronize_qmldir_optional_imports) +TEST_F(ProjectStorageUpdater, synchronize_qmldir_default_imports) { QString qmldir{R"(module Example import Qml auto import QML 2.1 - optional import Quick + default import Quick )"}; setContent(u"/path/qmldir", qmldir); @@ -1583,6 +1739,40 @@ TEST_F(ProjectStorageUpdater, synchronize_qmldir_optional_imports) updater.update(directories, {}, {}, {}); } +TEST_F(ProjectStorageUpdater, do_not_synchronize_qmldir_optional_imports) +{ + QString qmldir{R"(module Example + import Qml auto + import QML 2.1 + optional import Quick + )"}; + setContent(u"/path/qmldir", qmldir); + + EXPECT_CALL(projectStorageMock, + synchronize( + AllOf(Field(&SynchronizationPackage::moduleExportedImports, + UnorderedElementsAre(ModuleExportedImport{exampleModuleId, + qmlModuleId, + Storage::Version{}, + IsAutoVersion::Yes}, + ModuleExportedImport{exampleCppNativeModuleId, + qmlCppNativeModuleId, + Storage::Version{}, + IsAutoVersion::No}, + ModuleExportedImport{exampleModuleId, + builtinModuleId, + Storage::Version{2, 1}, + IsAutoVersion::No}, + ModuleExportedImport{exampleCppNativeModuleId, + builtinCppNativeModuleId, + Storage::Version{}, + IsAutoVersion::No})), + Field(&SynchronizationPackage::updatedModuleIds, + ElementsAre(exampleModuleId))))); + + updater.update(directories, {}, {}, {}); +} + TEST_F(ProjectStorageUpdater, update_path_watcher_directories) { EXPECT_CALL(patchWatcherMock, @@ -1712,11 +1902,11 @@ TEST_F(ProjectStorageUpdater, update_path_watcher_only_qml_files_changed) { setFilesDontChanged({qmldir1SourceId, qmldir2SourceId, path1SourceId, path2SourceId}); setFilesChanged({firstSourceId, secondSourceId, thirdSourceId}); - setProjectDatas(path1SourceId, - {{path1SourceId, firstSourceId, exampleModuleId, FileType::QmlDocument}, - {path1SourceId, secondSourceId, exampleModuleId, FileType::QmlDocument}}); - setProjectDatas(path2SourceId, - {{path2SourceId, thirdSourceId, ModuleId{}, FileType::QmlDocument}}); + setDirectoryInfos(path1SourceId, + {{path1SourceId, firstSourceId, exampleModuleId, FileType::QmlDocument}, + {path1SourceId, secondSourceId, exampleModuleId, FileType::QmlDocument}}); + setDirectoryInfos(path2SourceId, + {{path2SourceId, thirdSourceId, ModuleId{}, FileType::QmlDocument}}); EXPECT_CALL(patchWatcherMock, updateIdPaths(Contains(IdPaths{projectPartId, @@ -1735,11 +1925,11 @@ TEST_F(ProjectStorageUpdater, update_path_watcher_qml_files_and_directories_dont firstSourceId, secondSourceId, thirdSourceId}); - setProjectDatas(path1SourceId, - {{path1SourceId, firstSourceId, exampleModuleId, FileType::QmlDocument}, - {path1SourceId, secondSourceId, exampleModuleId, FileType::QmlDocument}}); - setProjectDatas(path2SourceId, - {{path2SourceId, thirdSourceId, ModuleId{}, FileType::QmlDocument}}); + setDirectoryInfos(path1SourceId, + {{path1SourceId, firstSourceId, exampleModuleId, FileType::QmlDocument}, + {path1SourceId, secondSourceId, exampleModuleId, FileType::QmlDocument}}); + setDirectoryInfos(path2SourceId, + {{path2SourceId, thirdSourceId, ModuleId{}, FileType::QmlDocument}}); EXPECT_CALL(patchWatcherMock, updateIdPaths(Contains(IdPaths{projectPartId, @@ -1790,10 +1980,10 @@ TEST_F(ProjectStorageUpdater, update_path_watcher_only_qmltypes_files_changed) { setFilesDontChanged({qmldir1SourceId, qmldir2SourceId, path1SourceId, path2SourceId}); setFilesChanged({qmltypes1SourceId, qmltypes2SourceId}); - setProjectDatas(path1SourceId, - {{path1SourceId, qmltypes1SourceId, exampleModuleId, FileType::QmlTypes}}); - setProjectDatas(path2SourceId, - {{path2SourceId, qmltypes2SourceId, exampleModuleId, FileType::QmlTypes}}); + setDirectoryInfos(path1SourceId, + {{path1SourceId, qmltypes1SourceId, exampleModuleId, FileType::QmlTypes}}); + setDirectoryInfos(path2SourceId, + {{path2SourceId, qmltypes2SourceId, exampleModuleId, FileType::QmlTypes}}); EXPECT_CALL(patchWatcherMock, updateIdPaths(Contains(IdPaths{projectPartId, @@ -1811,10 +2001,10 @@ TEST_F(ProjectStorageUpdater, update_path_watcher_qmltypes_files_and_directories path2SourceId, qmltypes1SourceId, qmltypes2SourceId}); - setProjectDatas(path1SourceId, - {{path1SourceId, qmltypes1SourceId, exampleModuleId, FileType::QmlTypes}}); - setProjectDatas(path2SourceId, - {{path2SourceId, qmltypes2SourceId, exampleModuleId, FileType::QmlTypes}}); + setDirectoryInfos(path1SourceId, + {{path1SourceId, qmltypes1SourceId, exampleModuleId, FileType::QmlTypes}}); + setDirectoryInfos(path2SourceId, + {{path2SourceId, qmltypes2SourceId, exampleModuleId, FileType::QmlTypes}}); EXPECT_CALL(patchWatcherMock, updateIdPaths(Contains(IdPaths{projectPartId, @@ -1887,21 +2077,21 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents_without_qmldir) IsFileStatus(qmlDocumentSourceId1, 1, 21), IsFileStatus(qmlDocumentSourceId2, 1, 21), IsFileStatus(qmlDocumentSourceId3, 1, 21))), - Field(&SynchronizationPackage::updatedProjectSourceIds, + Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds, UnorderedElementsAre(directoryPathSourceId)), - Field(&SynchronizationPackage::projectDatas, - UnorderedElementsAre(IsProjectData(directoryPathSourceId, - qmlDocumentSourceId1, - ModuleId{}, - FileType::QmlDocument), - IsProjectData(directoryPathSourceId, - qmlDocumentSourceId2, - ModuleId{}, - FileType::QmlDocument), - IsProjectData(directoryPathSourceId, - qmlDocumentSourceId3, - ModuleId{}, - FileType::QmlDocument)))))); + Field(&SynchronizationPackage::directoryInfos, + UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId1, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId2, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId3, + ModuleId{}, + FileType::QmlDocument)))))); updater.update(directories, {}, {}, {}); } @@ -1917,10 +2107,11 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents_without_qmldir_throws_if TEST_F(ProjectStorageUpdater, synchronize_qml_documents_without_qmldir_throws_if_directory_does_not_exists) { setFilesDontExists({qmlDirPathSourceId, directoryPathSourceId}); - setProjectDatas(directoryPathSourceId, - {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument}, - {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument}, - {directoryPathSourceId, qmlDocumentSourceId3, ModuleId{}, FileType::QmlDocument}}); + setDirectoryInfos( + directoryPathSourceId, + {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument}, + {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument}, + {directoryPathSourceId, qmlDocumentSourceId3, ModuleId{}, FileType::QmlDocument}}); EXPECT_CALL(projectStorageMock, synchronize(AllOf(Field(&SynchronizationPackage::imports, IsEmpty()), @@ -1937,9 +2128,9 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents_without_qmldir_throws_if qmlDocumentSourceId2, qmlDocumentSourceId3)), Field(&SynchronizationPackage::fileStatuses, IsEmpty()), - Field(&SynchronizationPackage::updatedProjectSourceIds, + Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds, UnorderedElementsAre(directoryPathSourceId)), - Field(&SynchronizationPackage::projectDatas, IsEmpty())))); + Field(&SynchronizationPackage::directoryInfos, IsEmpty())))); updater.update(directories, {}, {}, {}); } @@ -1950,9 +2141,9 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents_without_qmldir_add_qml_d setFilesChanged({directoryPathSourceId}); setFilesAdded({qmlDocumentSourceId3}); setFilesDontChanged({qmlDocumentSourceId1, qmlDocumentSourceId2}); - setProjectDatas(directoryPathSourceId, - {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument}, - {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument}}); + setDirectoryInfos(directoryPathSourceId, + {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument}, + {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument}}); EXPECT_CALL( projectStorageMock, @@ -1974,21 +2165,21 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents_without_qmldir_add_qml_d Field(&SynchronizationPackage::fileStatuses, UnorderedElementsAre(IsFileStatus(directoryPathSourceId, 1, 21), IsFileStatus(qmlDocumentSourceId3, 1, 21))), - Field(&SynchronizationPackage::updatedProjectSourceIds, + Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds, UnorderedElementsAre(directoryPathSourceId)), - Field(&SynchronizationPackage::projectDatas, - UnorderedElementsAre(IsProjectData(directoryPathSourceId, - qmlDocumentSourceId1, - ModuleId{}, - FileType::QmlDocument), - IsProjectData(directoryPathSourceId, - qmlDocumentSourceId2, - ModuleId{}, - FileType::QmlDocument), - IsProjectData(directoryPathSourceId, - qmlDocumentSourceId3, - ModuleId{}, - FileType::QmlDocument)))))); + Field(&SynchronizationPackage::directoryInfos, + UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId1, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId2, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId3, + ModuleId{}, + FileType::QmlDocument)))))); updater.update(directories, {}, {}, {}); } @@ -2000,10 +2191,11 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents_without_qmldir_removes_q setFilesRemoved({qmlDocumentSourceId3}); setFilesDontChanged({qmlDocumentSourceId1, qmlDocumentSourceId2}); setQmlFileNames(u"/path", {"First.qml", "First2.qml"}); - setProjectDatas(directoryPathSourceId, - {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument}, - {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument}, - {directoryPathSourceId, qmlDocumentSourceId3, ModuleId{}, FileType::QmlDocument}}); + setDirectoryInfos( + directoryPathSourceId, + {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument}, + {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument}, + {directoryPathSourceId, qmlDocumentSourceId3, ModuleId{}, FileType::QmlDocument}}); EXPECT_CALL(projectStorageMock, synchronize( @@ -2017,17 +2209,17 @@ TEST_F(ProjectStorageUpdater, synchronize_qml_documents_without_qmldir_removes_q qmlDocumentSourceId3)), Field(&SynchronizationPackage::fileStatuses, UnorderedElementsAre(IsFileStatus(directoryPathSourceId, 1, 21))), - Field(&SynchronizationPackage::updatedProjectSourceIds, + Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds, UnorderedElementsAre(directoryPathSourceId)), - Field(&SynchronizationPackage::projectDatas, - UnorderedElementsAre(IsProjectData(directoryPathSourceId, - qmlDocumentSourceId1, - ModuleId{}, - FileType::QmlDocument), - IsProjectData(directoryPathSourceId, - qmlDocumentSourceId2, - ModuleId{}, - FileType::QmlDocument)))))); + Field(&SynchronizationPackage::directoryInfos, + UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId1, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId2, + ModuleId{}, + FileType::QmlDocument)))))); updater.update(directories, {}, {}, {}); } @@ -2085,25 +2277,93 @@ TEST_F(ProjectStorageUpdater, watcher_updates_directories) IsFileStatus(qmlDocumentSourceId1, 1, 21), IsFileStatus(qmlDocumentSourceId2, 1, 21), IsFileStatus(qmlDocumentSourceId3, 1, 21))), - Field(&SynchronizationPackage::updatedProjectSourceIds, + Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds, UnorderedElementsAre(directoryPathSourceId)), - Field(&SynchronizationPackage::projectDatas, - UnorderedElementsAre(IsProjectData(directoryPathSourceId, - qmlDocumentSourceId1, - ModuleId{}, - FileType::QmlDocument), - IsProjectData(directoryPathSourceId, - qmlDocumentSourceId2, - ModuleId{}, - FileType::QmlDocument), - IsProjectData(directoryPathSourceId, - qmlDocumentSourceId3, - ModuleId{}, - FileType::QmlDocument)))))); + Field(&SynchronizationPackage::directoryInfos, + UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId1, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId2, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId3, + ModuleId{}, + FileType::QmlDocument)))))); updater.pathsWithIdsChanged({{directoryProjectChunkId, {directoryPathSourceId}}}); } +TEST_F(ProjectStorageUpdater, watcher_updates_subdirectories) +{ + QString qmldir{R"(module Example + FirstType 1.0 First.qml + FirstType 2.2 First2.qml + SecondType 2.2 Second.qml)"}; + setContent(u"/path/qmldir", qmldir); + SourceId rootPathSourceId = sourcePathCache.sourceId("/root/."); + SourceId rootQmldirPathSourceId = sourcePathCache.sourceId("/root/qmldir"); + setFilesChanged({directoryPathSourceId, rootPathSourceId}); + setFilesDontChanged({qmlDirPathSourceId, rootQmldirPathSourceId}); + setSubdirectoryPaths(u"/root", {"/path"}); + setSubdirectorySourceIds(rootPathSourceId, {directoryPathSourceId}); + + EXPECT_CALL( + projectStorageMock, + synchronize(AllOf( + Field(&SynchronizationPackage::imports, UnorderedElementsAre(import1, import2, import3)), + Field( + &SynchronizationPackage::types, + UnorderedElementsAre( + AllOf(IsStorageType("First.qml", + Storage::Synchronization::ImportedType{"Object"}, + TypeTraitsKind::Reference, + qmlDocumentSourceId1, + Storage::Synchronization::ChangeLevel::Full), + Field(&Storage::Synchronization::Type::exportedTypes, + UnorderedElementsAre(IsExportedType(exampleModuleId, "FirstType", 1, 0), + IsExportedType(pathModuleId, "First", -1, -1)))), + AllOf(IsStorageType("First2.qml", + Storage::Synchronization::ImportedType{"Object2"}, + TypeTraitsKind::Reference, + qmlDocumentSourceId2, + Storage::Synchronization::ChangeLevel::Full), + Field(&Storage::Synchronization::Type::exportedTypes, + UnorderedElementsAre(IsExportedType(exampleModuleId, "FirstType", 2, 2), + IsExportedType(pathModuleId, "First2", -1, -1)))), + AllOf(IsStorageType("Second.qml", + Storage::Synchronization::ImportedType{"Object3"}, + TypeTraitsKind::Reference, + qmlDocumentSourceId3, + Storage::Synchronization::ChangeLevel::Full), + Field(&Storage::Synchronization::Type::exportedTypes, + UnorderedElementsAre(IsExportedType(exampleModuleId, "SecondType", 2, 2), + IsExportedType(pathModuleId, "Second", -1, -1)))))), + Field(&SynchronizationPackage::updatedSourceIds, + UnorderedElementsAre(qmlDocumentSourceId1, qmlDocumentSourceId2, qmlDocumentSourceId3)), + Field(&SynchronizationPackage::directoryInfos, + UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId1, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId2, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId3, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(rootPathSourceId, + directoryPathSourceId, + ModuleId{}, + FileType::Directory)))))); + + updater.pathsWithIdsChanged({{directoryProjectChunkId, {rootPathSourceId, directoryPathSourceId}}}); +} + TEST_F(ProjectStorageUpdater, watcher_updates_removed_directory) { setFilesRemoved({directoryPathSourceId, @@ -2111,10 +2371,11 @@ TEST_F(ProjectStorageUpdater, watcher_updates_removed_directory) qmlDocumentSourceId1, qmlDocumentSourceId2, qmlDocumentSourceId3}); - setProjectDatas(directoryPathSourceId, - {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument}, - {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument}, - {directoryPathSourceId, qmlDocumentSourceId3, ModuleId{}, FileType::QmlDocument}}); + setDirectoryInfos( + directoryPathSourceId, + {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument}, + {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument}, + {directoryPathSourceId, qmlDocumentSourceId3, ModuleId{}, FileType::QmlDocument}}); EXPECT_CALL(projectStorageMock, synchronize(AllOf(Field(&SynchronizationPackage::imports, IsEmpty()), @@ -2131,9 +2392,9 @@ TEST_F(ProjectStorageUpdater, watcher_updates_removed_directory) qmlDocumentSourceId2, qmlDocumentSourceId3)), Field(&SynchronizationPackage::fileStatuses, UnorderedElementsAre()), - Field(&SynchronizationPackage::updatedProjectSourceIds, + Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds, UnorderedElementsAre(directoryPathSourceId)), - Field(&SynchronizationPackage::projectDatas, IsEmpty())))); + Field(&SynchronizationPackage::directoryInfos, IsEmpty())))); updater.pathsWithIdsChanged({{directoryProjectChunkId, {directoryPathSourceId}}}); } @@ -2233,21 +2494,21 @@ TEST_F(ProjectStorageUpdater, watcher_updates_directories_and_qmldir) IsFileStatus(qmlDocumentSourceId1, 1, 21), IsFileStatus(qmlDocumentSourceId2, 1, 21), IsFileStatus(qmlDocumentSourceId3, 1, 21))), - Field(&SynchronizationPackage::updatedProjectSourceIds, + Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds, UnorderedElementsAre(directoryPathSourceId)), - Field(&SynchronizationPackage::projectDatas, - UnorderedElementsAre(IsProjectData(directoryPathSourceId, - qmlDocumentSourceId1, - ModuleId{}, - FileType::QmlDocument), - IsProjectData(directoryPathSourceId, - qmlDocumentSourceId2, - ModuleId{}, - FileType::QmlDocument), - IsProjectData(directoryPathSourceId, - qmlDocumentSourceId3, - ModuleId{}, - FileType::QmlDocument)))))); + Field(&SynchronizationPackage::directoryInfos, + UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId1, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId2, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId3, + ModuleId{}, + FileType::QmlDocument)))))); updater.pathsWithIdsChanged({{directoryProjectChunkId, {directoryPathSourceId}}, {qmldirProjectChunkId, {qmlDirPathSourceId}}}); @@ -2297,8 +2558,8 @@ TEST_F(ProjectStorageUpdater, watcher_updates_add_only_qml_document_in_directory setFilesChanged({directoryPathSourceId}); setFilesDontChanged({qmlDirPathSourceId, qmlDocumentSourceId1}); setFilesAdded({qmlDocumentSourceId2}); - setProjectDatas(directoryPathSourceId, - {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument}}); + setDirectoryInfos(directoryPathSourceId, + {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument}}); setQmlFileNames(u"/path", {"First.qml", "First2.qml"}); EXPECT_CALL(projectStorageMock, @@ -2330,17 +2591,17 @@ TEST_F(ProjectStorageUpdater, watcher_updates_add_only_qml_document_in_directory Field(&SynchronizationPackage::fileStatuses, UnorderedElementsAre(IsFileStatus(qmlDocumentSourceId2, 1, 21), IsFileStatus(directoryPathSourceId, 1, 21))), - Field(&SynchronizationPackage::updatedProjectSourceIds, + Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds, UnorderedElementsAre(directoryPathSourceId)), - Field(&SynchronizationPackage::projectDatas, - UnorderedElementsAre(IsProjectData(directoryPathSourceId, - qmlDocumentSourceId1, - ModuleId{}, - FileType::QmlDocument), - IsProjectData(directoryPathSourceId, - qmlDocumentSourceId2, - ModuleId{}, - FileType::QmlDocument)))))); + Field(&SynchronizationPackage::directoryInfos, + UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId1, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId2, + ModuleId{}, + FileType::QmlDocument)))))); updater.pathsWithIdsChanged({{directoryProjectChunkId, {directoryPathSourceId}}}); } @@ -2355,10 +2616,11 @@ TEST_F(ProjectStorageUpdater, watcher_updates_removes_qml_document) setFilesChanged({qmlDirPathSourceId}); setFilesDontChanged({qmlDocumentSourceId1, qmlDocumentSourceId2}); setFilesRemoved({qmlDocumentSourceId3}); - setProjectDatas(directoryPathSourceId, - {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument}, - {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument}, - {directoryPathSourceId, qmlDocumentSourceId3, ModuleId{}, FileType::QmlDocument}}); + setDirectoryInfos( + directoryPathSourceId, + {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument}, + {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument}, + {directoryPathSourceId, qmlDocumentSourceId3, ModuleId{}, FileType::QmlDocument}}); setQmlFileNames(u"/path", {"First.qml", "First2.qml"}); EXPECT_CALL(projectStorageMock, @@ -2393,17 +2655,17 @@ TEST_F(ProjectStorageUpdater, watcher_updates_removes_qml_document) UnorderedElementsAre(qmlDirPathSourceId, qmlDocumentSourceId3)), Field(&SynchronizationPackage::fileStatuses, UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 1, 21))), - Field(&SynchronizationPackage::updatedProjectSourceIds, + Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds, UnorderedElementsAre(directoryPathSourceId)), - Field(&SynchronizationPackage::projectDatas, - UnorderedElementsAre(IsProjectData(directoryPathSourceId, - qmlDocumentSourceId1, - ModuleId{}, - FileType::QmlDocument), - IsProjectData(directoryPathSourceId, - qmlDocumentSourceId2, - ModuleId{}, - FileType::QmlDocument)))))); + Field(&SynchronizationPackage::directoryInfos, + UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId1, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId2, + ModuleId{}, + FileType::QmlDocument)))))); updater.pathsWithIdsChanged({{directoryProjectChunkId, {directoryPathSourceId}}}); } @@ -2416,9 +2678,9 @@ TEST_F(ProjectStorageUpdater, watcher_updates_removes_qml_document_in_qmldir_onl setContent(u"/path/qmldir", qmldir); setFilesChanged({qmlDirPathSourceId}); setFilesDontChanged({qmlDocumentSourceId1, qmlDocumentSourceId2}); - setProjectDatas(directoryPathSourceId, - {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument}, - {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument}}); + setDirectoryInfos(directoryPathSourceId, + {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument}, + {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument}}); setQmlFileNames(u"/path", {"First.qml", "First2.qml"}); EXPECT_CALL( @@ -2448,17 +2710,17 @@ TEST_F(ProjectStorageUpdater, watcher_updates_removes_qml_document_in_qmldir_onl UnorderedElementsAre(qmlDirPathSourceId)), Field(&SynchronizationPackage::fileStatuses, UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 1, 21))), - Field(&SynchronizationPackage::updatedProjectSourceIds, + Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds, UnorderedElementsAre(directoryPathSourceId)), - Field(&SynchronizationPackage::projectDatas, - UnorderedElementsAre(IsProjectData(directoryPathSourceId, - qmlDocumentSourceId1, - ModuleId{}, - FileType::QmlDocument), - IsProjectData(directoryPathSourceId, - qmlDocumentSourceId2, - ModuleId{}, - FileType::QmlDocument)))))); + Field(&SynchronizationPackage::directoryInfos, + UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId1, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId2, + ModuleId{}, + FileType::QmlDocument)))))); updater.pathsWithIdsChanged({{qmldirProjectChunkId, {qmlDirPathSourceId}}}); } @@ -2472,9 +2734,9 @@ TEST_F(ProjectStorageUpdater, watcher_updates_directories_add_qml_document_to_qm setContent(u"/path/qmldir", qmldir); setFilesChanged({qmlDirPathSourceId}); setFilesDontChanged({qmlDocumentSourceId1, qmlDocumentSourceId2}); - setProjectDatas(directoryPathSourceId, - {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument}, - {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument}}); + setDirectoryInfos(directoryPathSourceId, + {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument}, + {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument}}); setQmlFileNames(u"/path", {"First.qml", "First2.qml"}); EXPECT_CALL( @@ -2506,17 +2768,17 @@ TEST_F(ProjectStorageUpdater, watcher_updates_directories_add_qml_document_to_qm UnorderedElementsAre(qmlDirPathSourceId)), Field(&SynchronizationPackage::fileStatuses, UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 1, 21))), - Field(&SynchronizationPackage::updatedProjectSourceIds, + Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds, UnorderedElementsAre(directoryPathSourceId)), - Field(&SynchronizationPackage::projectDatas, - UnorderedElementsAre(IsProjectData(directoryPathSourceId, - qmlDocumentSourceId1, - ModuleId{}, - FileType::QmlDocument), - IsProjectData(directoryPathSourceId, - qmlDocumentSourceId2, - ModuleId{}, - FileType::QmlDocument)))))); + Field(&SynchronizationPackage::directoryInfos, + UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId1, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId2, + ModuleId{}, + FileType::QmlDocument)))))); updater.pathsWithIdsChanged({{qmldirProjectChunkId, {qmlDirPathSourceId}}}); } @@ -2529,9 +2791,9 @@ TEST_F(ProjectStorageUpdater, watcher_updates_directories_remove_qml_document_fr setContent(u"/path/qmldir", qmldir); setFilesDontChanged({qmlDocumentSourceId1, qmlDocumentSourceId2}); setFilesChanged({qmlDirPathSourceId}); - setProjectDatas(directoryPathSourceId, - {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument}, - {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument}}); + setDirectoryInfos(directoryPathSourceId, + {{directoryPathSourceId, qmlDocumentSourceId1, ModuleId{}, FileType::QmlDocument}, + {directoryPathSourceId, qmlDocumentSourceId2, ModuleId{}, FileType::QmlDocument}}); setQmlFileNames(u"/path", {"First.qml", "First2.qml"}); EXPECT_CALL( @@ -2561,17 +2823,17 @@ TEST_F(ProjectStorageUpdater, watcher_updates_directories_remove_qml_document_fr UnorderedElementsAre(qmlDirPathSourceId)), Field(&SynchronizationPackage::fileStatuses, UnorderedElementsAre(IsFileStatus(qmlDirPathSourceId, 1, 21))), - Field(&SynchronizationPackage::updatedProjectSourceIds, + Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds, UnorderedElementsAre(directoryPathSourceId)), - Field(&SynchronizationPackage::projectDatas, - UnorderedElementsAre(IsProjectData(directoryPathSourceId, - qmlDocumentSourceId1, - ModuleId{}, - FileType::QmlDocument), - IsProjectData(directoryPathSourceId, - qmlDocumentSourceId2, - ModuleId{}, - FileType::QmlDocument)))))); + Field(&SynchronizationPackage::directoryInfos, + UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId1, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId2, + ModuleId{}, + FileType::QmlDocument)))))); updater.pathsWithIdsChanged({{qmldirProjectChunkId, {qmlDirPathSourceId}}}); } @@ -2627,21 +2889,21 @@ TEST_F(ProjectStorageUpdater, watcher_updates_directories_dont_update_qml_docume IsFileStatus(qmlDocumentSourceId2, 1, 21))), Field(&SynchronizationPackage::updatedFileStatusSourceIds, UnorderedElementsAre(qmlDirPathSourceId, qmlDocumentSourceId1, qmlDocumentSourceId2)), - Field(&SynchronizationPackage::updatedProjectSourceIds, + Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds, UnorderedElementsAre(directoryPathSourceId)), - Field(&SynchronizationPackage::projectDatas, - UnorderedElementsAre(IsProjectData(directoryPathSourceId, - qmlDocumentSourceId1, - ModuleId{}, - FileType::QmlDocument), - IsProjectData(directoryPathSourceId, - qmlDocumentSourceId2, - ModuleId{}, - FileType::QmlDocument), - IsProjectData(directoryPathSourceId, - qmlDocumentSourceId3, - ModuleId{}, - FileType::QmlDocument)))))); + Field(&SynchronizationPackage::directoryInfos, + UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId1, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId2, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId3, + ModuleId{}, + FileType::QmlDocument)))))); updater.pathsWithIdsChanged({{directoryProjectChunkId, {directoryPathSourceId}}}); } @@ -2697,28 +2959,28 @@ TEST_F(ProjectStorageUpdater, watcher_updates_qmldirs_dont_update_qml_documents_ IsFileStatus(qmlDocumentSourceId2, 1, 21))), Field(&SynchronizationPackage::updatedFileStatusSourceIds, UnorderedElementsAre(qmlDirPathSourceId, qmlDocumentSourceId1, qmlDocumentSourceId2)), - Field(&SynchronizationPackage::updatedProjectSourceIds, + Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds, UnorderedElementsAre(directoryPathSourceId)), - Field(&SynchronizationPackage::projectDatas, - UnorderedElementsAre(IsProjectData(directoryPathSourceId, - qmlDocumentSourceId1, - ModuleId{}, - FileType::QmlDocument), - IsProjectData(directoryPathSourceId, - qmlDocumentSourceId2, - ModuleId{}, - FileType::QmlDocument), - IsProjectData(directoryPathSourceId, - qmlDocumentSourceId3, - ModuleId{}, - FileType::QmlDocument)))))); + Field(&SynchronizationPackage::directoryInfos, + UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId1, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId2, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId3, + ModuleId{}, + FileType::QmlDocument)))))); updater.pathsWithIdsChanged({{qmldirProjectChunkId, {qmlDirPathSourceId}}}); } TEST_F(ProjectStorageUpdater, watcher_updates_directory_but_not_qmldir) { - setProjectDatas( + setDirectoryInfos( directoryPathSourceId, {{directoryPathSourceId, qmltypesPathSourceId, exampleModuleId, FileType::QmlTypes}, {directoryPathSourceId, qmltypes2PathSourceId, exampleModuleId, FileType::QmlTypes}, @@ -2762,7 +3024,7 @@ TEST_F(ProjectStorageUpdater, watcher_updates_directory_but_not_qmldir) qmltypes2PathSourceId, qmlDocumentSourceId1, qmlDocumentSourceId2)), - Field(&SynchronizationPackage::projectDatas, IsEmpty())))); + Field(&SynchronizationPackage::directoryInfos, IsEmpty())))); updater.pathsWithIdsChanged({{directoryProjectChunkId, {directoryPathSourceId}}}); } @@ -2794,7 +3056,7 @@ TEST_F(ProjectStorageUpdater, watcher_updates_qml_documents) IsFileStatus(qmlDocumentSourceId2, 1, 21))), Field(&SynchronizationPackage::updatedFileStatusSourceIds, UnorderedElementsAre(qmlDocumentSourceId1, qmlDocumentSourceId2)), - Field(&SynchronizationPackage::projectDatas, IsEmpty())))); + Field(&SynchronizationPackage::directoryInfos, IsEmpty())))); updater.pathsWithIdsChanged( {{qmlDocumentProjectChunkId, {qmlDocumentSourceId1, qmlDocumentSourceId2}}}); @@ -2821,7 +3083,7 @@ TEST_F(ProjectStorageUpdater, watcher_updates_removed_qml_documents) UnorderedElementsAre(IsFileStatus(qmlDocumentSourceId1, 1, 21))), Field(&SynchronizationPackage::updatedFileStatusSourceIds, UnorderedElementsAre(qmlDocumentSourceId1, qmlDocumentSourceId2)), - Field(&SynchronizationPackage::projectDatas, IsEmpty())))); + Field(&SynchronizationPackage::directoryInfos, IsEmpty())))); updater.pathsWithIdsChanged( {{qmlDocumentProjectChunkId, {qmlDocumentSourceId1, qmlDocumentSourceId2}}}); @@ -2844,7 +3106,7 @@ TEST_F(ProjectStorageUpdater, watcher_dont_updates_qml_documents_for_other_proje TEST_F(ProjectStorageUpdater, watcher_updates_qmltypes) { - setProjectDatas( + setDirectoryInfos( directoryPathSourceId, {{directoryPathSourceId, qmltypesPathSourceId, exampleModuleId, FileType::QmlTypes}, {directoryPathSourceId, qmltypes2PathSourceId, exampleModuleId, FileType::QmlTypes}}); @@ -2864,7 +3126,7 @@ TEST_F(ProjectStorageUpdater, watcher_updates_qmltypes) IsFileStatus(qmltypes2PathSourceId, 1, 21))), Field(&SynchronizationPackage::updatedFileStatusSourceIds, UnorderedElementsAre(qmltypesPathSourceId, qmltypes2PathSourceId)), - Field(&SynchronizationPackage::projectDatas, IsEmpty())))); + Field(&SynchronizationPackage::directoryInfos, IsEmpty())))); updater.pathsWithIdsChanged( {{qmltypesProjectChunkId, {qmltypesPathSourceId, qmltypes2PathSourceId}}}); @@ -2872,7 +3134,7 @@ TEST_F(ProjectStorageUpdater, watcher_updates_qmltypes) TEST_F(ProjectStorageUpdater, watcher_updates_removed_qmltypes_without_updated_qmldir) { - setProjectDatas( + setDirectoryInfos( directoryPathSourceId, {{directoryPathSourceId, qmltypesPathSourceId, exampleModuleId, FileType::QmlTypes}, {directoryPathSourceId, qmltypes2PathSourceId, exampleModuleId, FileType::QmlTypes}}); @@ -2888,7 +3150,7 @@ TEST_F(ProjectStorageUpdater, watcher_updates_removed_qmltypes_without_updated_q TEST_F(ProjectStorageUpdater, watcher_updates_removed_qmltypes_with_updated_qmldir) { - setProjectDatas( + setDirectoryInfos( directoryPathSourceId, {{directoryPathSourceId, qmltypesPathSourceId, exampleModuleId, FileType::QmlTypes}, {directoryPathSourceId, qmltypes2PathSourceId, exampleModuleId, FileType::QmlTypes}}); @@ -2918,18 +3180,18 @@ TEST_F(ProjectStorageUpdater, watcher_updates_removed_qmltypes_with_updated_qmld UnorderedElementsAre(qmlDirPathSourceId, qmltypesPathSourceId, qmltypes2PathSourceId)), - Field(&SynchronizationPackage::projectDatas, - UnorderedElementsAre(IsProjectData(directoryPathSourceId, - qmltypes2PathSourceId, - exampleCppNativeModuleId, - FileType::QmlTypes)))))); + Field(&SynchronizationPackage::directoryInfos, + UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, + qmltypes2PathSourceId, + exampleCppNativeModuleId, + FileType::QmlTypes)))))); updater.pathsWithIdsChanged({{qmldirProjectChunkId, {qmlDirPathSourceId}}}); } TEST_F(ProjectStorageUpdater, watcher_dont_watches_directories_after_qmltypes_changes) { - setProjectDatas( + setDirectoryInfos( directoryPathSourceId, {{directoryPathSourceId, qmltypesPathSourceId, exampleModuleId, FileType::QmlTypes}, {directoryPathSourceId, qmltypes2PathSourceId, exampleModuleId, FileType::QmlTypes}}); @@ -2944,7 +3206,7 @@ TEST_F(ProjectStorageUpdater, watcher_dont_watches_directories_after_qmltypes_ch TEST_F(ProjectStorageUpdater, watcher_dont_updates_qmltypes_for_other_projects) { - setProjectDatas( + setDirectoryInfos( directoryPathSourceId, {{directoryPathSourceId, qmltypesPathSourceId, exampleModuleId, FileType::QmlTypes}, {directoryPathSourceId, qmltypes2PathSourceId, exampleModuleId, FileType::QmlTypes}}); @@ -3010,21 +3272,21 @@ TEST_F(ProjectStorageUpdater, watcher_updates_directories_and_but_not_included_q IsFileStatus(qmlDocumentSourceId1, 1, 21), IsFileStatus(qmlDocumentSourceId2, 1, 21), IsFileStatus(qmlDocumentSourceId3, 1, 21))), - Field(&SynchronizationPackage::updatedProjectSourceIds, + Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds, UnorderedElementsAre(directoryPathSourceId)), - Field(&SynchronizationPackage::projectDatas, - UnorderedElementsAre(IsProjectData(directoryPathSourceId, - qmlDocumentSourceId1, - ModuleId{}, - FileType::QmlDocument), - IsProjectData(directoryPathSourceId, - qmlDocumentSourceId2, - ModuleId{}, - FileType::QmlDocument), - IsProjectData(directoryPathSourceId, - qmlDocumentSourceId3, - ModuleId{}, - FileType::QmlDocument)))))); + Field(&SynchronizationPackage::directoryInfos, + UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId1, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId2, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId3, + ModuleId{}, + FileType::QmlDocument)))))); updater.pathsWithIdsChanged({{directoryProjectChunkId, {directoryPathSourceId}}, {qmlDocumentProjectChunkId, @@ -3087,21 +3349,21 @@ TEST_F(ProjectStorageUpdater, watcher_updates_qmldir_and_but_not_included_qml_do IsFileStatus(qmlDocumentSourceId1, 1, 21), IsFileStatus(qmlDocumentSourceId2, 1, 21), IsFileStatus(qmlDocumentSourceId3, 1, 21))), - Field(&SynchronizationPackage::updatedProjectSourceIds, + Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds, UnorderedElementsAre(directoryPathSourceId)), - Field(&SynchronizationPackage::projectDatas, - UnorderedElementsAre(IsProjectData(directoryPathSourceId, - qmlDocumentSourceId1, - ModuleId{}, - FileType::QmlDocument), - IsProjectData(directoryPathSourceId, - qmlDocumentSourceId2, - ModuleId{}, - FileType::QmlDocument), - IsProjectData(directoryPathSourceId, - qmlDocumentSourceId3, - ModuleId{}, - FileType::QmlDocument)))))); + Field(&SynchronizationPackage::directoryInfos, + UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId1, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId2, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId3, + ModuleId{}, + FileType::QmlDocument)))))); updater.pathsWithIdsChanged({{qmldirProjectChunkId, {qmlDirPathSourceId}}, {qmlDocumentProjectChunkId, @@ -3110,7 +3372,7 @@ TEST_F(ProjectStorageUpdater, watcher_updates_qmldir_and_but_not_included_qml_do TEST_F(ProjectStorageUpdater, watcher_updates_qmldir_and_but_not_included_qmltypes) { - setProjectDatas( + setDirectoryInfos( directoryPathSourceId, {{directoryPathSourceId, qmltypesPathSourceId, exampleModuleId, FileType::QmlTypes}, {directoryPathSourceId, qmltypes2PathSourceId, exampleModuleId, FileType::QmlTypes}, @@ -3186,29 +3448,29 @@ TEST_F(ProjectStorageUpdater, watcher_updates_qmldir_and_but_not_included_qmltyp IsFileStatus(qmlDocumentSourceId1, 1, 21), IsFileStatus(qmlDocumentSourceId2, 1, 21), IsFileStatus(qmlDocumentSourceId3, 1, 21))), - Field(&SynchronizationPackage::updatedProjectSourceIds, + Field(&SynchronizationPackage::updatedDirectoryInfoSourceIds, UnorderedElementsAre(directoryPathSourceId)), - Field(&SynchronizationPackage::projectDatas, - UnorderedElementsAre(IsProjectData(directoryPathSourceId, - qmltypesPathSourceId, - exampleCppNativeModuleId, - FileType::QmlTypes), - IsProjectData(directoryPathSourceId, - qmltypes2PathSourceId, - exampleCppNativeModuleId, - FileType::QmlTypes), - IsProjectData(directoryPathSourceId, - qmlDocumentSourceId1, - ModuleId{}, - FileType::QmlDocument), - IsProjectData(directoryPathSourceId, - qmlDocumentSourceId2, - ModuleId{}, - FileType::QmlDocument), - IsProjectData(directoryPathSourceId, - qmlDocumentSourceId3, - ModuleId{}, - FileType::QmlDocument)))))); + Field(&SynchronizationPackage::directoryInfos, + UnorderedElementsAre(IsDirectoryInfo(directoryPathSourceId, + qmltypesPathSourceId, + exampleCppNativeModuleId, + FileType::QmlTypes), + IsDirectoryInfo(directoryPathSourceId, + qmltypes2PathSourceId, + exampleCppNativeModuleId, + FileType::QmlTypes), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId1, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId2, + ModuleId{}, + FileType::QmlDocument), + IsDirectoryInfo(directoryPathSourceId, + qmlDocumentSourceId3, + ModuleId{}, + FileType::QmlDocument)))))); updater.pathsWithIdsChanged( {{qmldirProjectChunkId, {qmlDirPathSourceId}}, @@ -3236,7 +3498,7 @@ TEST_F(ProjectStorageUpdater, input_is_reused_next_call_if_an_error_happens) FirstType 2.2 First2.qml SecondType 2.2 Second.qml)"}; setContent(u"/path/qmldir", qmldir); - setProjectDatas( + setDirectoryInfos( directoryPathSourceId, {{directoryPathSourceId, qmltypesPathSourceId, exampleModuleId, FileType::QmlTypes}, {directoryPathSourceId, qmltypes2PathSourceId, exampleModuleId, FileType::QmlTypes}}); @@ -3283,7 +3545,7 @@ TEST_F(ProjectStorageUpdater, input_is_reused_next_call_if_an_error_happens) qmlDocumentSourceId2, qmltypesPathSourceId, qmltypes2PathSourceId)), - Field(&SynchronizationPackage::projectDatas, IsEmpty())))); + Field(&SynchronizationPackage::directoryInfos, IsEmpty())))); updater.pathsWithIdsChanged( {{qmlDocumentProjectChunkId, {qmlDocumentSourceId1, qmlDocumentSourceId2}}}); @@ -3296,7 +3558,7 @@ TEST_F(ProjectStorageUpdater, input_is_reused_next_call_if_an_error_happens_and_ FirstType 2.2 First2.qml SecondType 2.2 Second.qml)"}; setContent(u"/path/qmldir", qmldir); - setProjectDatas( + setDirectoryInfos( directoryPathSourceId, {{directoryPathSourceId, qmltypesPathSourceId, exampleModuleId, FileType::QmlTypes}, {directoryPathSourceId, qmltypes2PathSourceId, exampleModuleId, FileType::QmlTypes}}); @@ -3343,7 +3605,7 @@ TEST_F(ProjectStorageUpdater, input_is_reused_next_call_if_an_error_happens_and_ qmlDocumentSourceId2, qmltypesPathSourceId, qmltypes2PathSourceId)), - Field(&SynchronizationPackage::projectDatas, IsEmpty())))); + Field(&SynchronizationPackage::directoryInfos, IsEmpty())))); updater.pathsWithIdsChanged( {{qmltypesProjectChunkId, {qmltypesPathSourceId, qmltypes2PathSourceId}}, @@ -3357,7 +3619,7 @@ TEST_F(ProjectStorageUpdater, input_is_reused_next_call_if_an_error_happens_and_ FirstType 2.2 First2.qml SecondType 2.2 Second.qml)"}; setContent(u"/path/qmldir", qmldir); - setProjectDatas( + setDirectoryInfos( directoryPathSourceId, {{directoryPathSourceId, qmltypesPathSourceId, exampleModuleId, FileType::QmlTypes}, {directoryPathSourceId, qmltypes2PathSourceId, exampleModuleId, FileType::QmlTypes}, @@ -3406,7 +3668,7 @@ TEST_F(ProjectStorageUpdater, input_is_reused_next_call_if_an_error_happens_and_ qmlDocumentSourceId2, qmltypesPathSourceId, qmltypes2PathSourceId)), - Field(&SynchronizationPackage::projectDatas, IsEmpty())))); + Field(&SynchronizationPackage::directoryInfos, IsEmpty())))); updater.pathsWithIdsChanged( {{qmltypesProjectChunkId, {qmltypesPathSourceId, qmltypes2PathSourceId}}, @@ -3420,7 +3682,7 @@ TEST_F(ProjectStorageUpdater, input_is_cleared_after_successful_update) FirstType 2.2 First2.qml SecondType 2.2 Second.qml)"}; setContent(u"/path/qmldir", qmldir); - setProjectDatas( + setDirectoryInfos( directoryPathSourceId, {{directoryPathSourceId, qmltypesPathSourceId, exampleModuleId, FileType::QmlTypes}, {directoryPathSourceId, qmltypes2PathSourceId, exampleModuleId, FileType::QmlTypes}}); @@ -3453,7 +3715,7 @@ TEST_F(ProjectStorageUpdater, input_is_cleared_after_successful_update) IsFileStatus(qmlDocumentSourceId2, 1, 21))), Field(&SynchronizationPackage::updatedFileStatusSourceIds, UnorderedElementsAre(qmlDocumentSourceId1, qmlDocumentSourceId2)), - Field(&SynchronizationPackage::projectDatas, IsEmpty())))); + Field(&SynchronizationPackage::directoryInfos, IsEmpty())))); updater.pathsWithIdsChanged( {{qmlDocumentProjectChunkId, {qmlDocumentSourceId1, qmlDocumentSourceId2}}}); @@ -3475,7 +3737,7 @@ TEST_F(ProjectStorageUpdater, update_property_editor_panes) auto directoryId = sourcePathCache.sourceId( QmlDesigner::SourcePath{propertyEditorQmlPath + "/QML/."}); setFilesChanged({directoryId}); - auto qmlModuleId = storage.moduleId("QML"); + auto qmlModuleId = storage.moduleId("QML", ModuleKind::QmlLibrary); EXPECT_CALL(projectStorageMock, synchronize( @@ -3499,19 +3761,26 @@ TEST_F(ProjectStorageUpdater, update_property_editor_specifics) ON_CALL(projectStorageMock, fetchFileStatus(_)).WillByDefault([](SourceId sourceId) { return FileStatus{sourceId, 1, 21}; }); - auto sourceId = sourcePathCache.sourceId( + auto textSourceId = sourcePathCache.sourceId( QmlDesigner::SourcePath{propertyEditorQmlPath + "/QtQuick/TextSpecifics.qml"}); - auto directoryId = sourcePathCache.sourceId( + auto qtQuickDirectoryId = sourcePathCache.sourceId( QmlDesigner::SourcePath{propertyEditorQmlPath + "/QtQuick/."}); - setFilesChanged({directoryId}); - auto qtQuickModuleId = storage.moduleId("QtQuick"); + auto buttonSourceId = sourcePathCache.sourceId( + QmlDesigner::SourcePath{propertyEditorQmlPath + "/QtQuick/Controls/ButtonSpecifics.qml"}); + auto controlsDirectoryId = sourcePathCache.sourceId( + QmlDesigner::SourcePath{propertyEditorQmlPath + "/QtQuick/Controls/."}); + setFilesChanged({qtQuickDirectoryId, controlsDirectoryId}); + auto qtQuickModuleId = storage.moduleId("QtQuick", ModuleKind::QmlLibrary); + auto controlsModuleId = storage.moduleId("QtQuick.Controls", ModuleKind::QmlLibrary); EXPECT_CALL(projectStorageMock, - synchronize( - AllOf(Field(&SynchronizationPackage::propertyEditorQmlPaths, - Contains(IsPropertyEditorQmlPath(qtQuickModuleId, "Text", sourceId))), - Field(&SynchronizationPackage::updatedPropertyEditorQmlPathSourceIds, - ElementsAre(directoryId))))); + synchronize(AllOf( + Field(&SynchronizationPackage::propertyEditorQmlPaths, + IsSupersetOf( + {IsPropertyEditorQmlPath(qtQuickModuleId, "Text", textSourceId), + IsPropertyEditorQmlPath(controlsModuleId, "Button", buttonSourceId)})), + Field(&SynchronizationPackage::updatedPropertyEditorQmlPathSourceIds, + ElementsAre(qtQuickDirectoryId, controlsDirectoryId))))); updater.update({}, {}, propertyEditorQmlPath, {}); } @@ -3538,8 +3807,8 @@ TEST_F(ProjectStorageUpdater, update_type_annotations) auto buttonSourceId = sourcePathCache.sourceId( QmlDesigner::SourcePath{itemLibraryPath + "/qtquickcontrols2.metainfo"}); setFilesChanged({itemLibraryPathSourceId, itemSourceId, buttonSourceId}); - auto qtQuickModuleId = moduleId("QtQuick"); - auto qtQuickControlsModuleId = moduleId("QtQuick.Controls.Basic"); + auto qtQuickModuleId = moduleId("QtQuick", ModuleKind::QmlLibrary); + auto qtQuickControlsModuleId = moduleId("QtQuick.Controls.Basic", ModuleKind::QmlLibrary); QmlDesigner::Storage::TypeTraits itemTraits; itemTraits.canBeContainer = QmlDesigner::FlagIs::True; @@ -3575,8 +3844,8 @@ TEST_F(ProjectStorageUpdater, update_changed_type_annotation) QmlDesigner::SourcePath{itemLibraryPath + "/qtquickcontrols2.metainfo"}); setFilesDontChanged({itemLibraryPathSourceId}); setFilesChanged({itemSourceId, buttonSourceId}); - auto qtQuickModuleId = moduleId("QtQuick"); - auto qtQuickControlsModuleId = moduleId("QtQuick.Controls.Basic"); + auto qtQuickModuleId = moduleId("QtQuick", ModuleKind::QmlLibrary); + auto qtQuickControlsModuleId = moduleId("QtQuick.Controls.Basic", ModuleKind::QmlLibrary); QmlDesigner::Storage::TypeTraits itemTraits; itemTraits.canBeContainer = QmlDesigner::FlagIs::True; diff --git a/tests/unit/tests/unittests/projectstorage/qmldocumentparser-test.cpp b/tests/unit/tests/unittests/projectstorage/qmldocumentparser-test.cpp index 513fbf2ec01..e135bb27bd3 100644 --- a/tests/unit/tests/unittests/projectstorage/qmldocumentparser-test.cpp +++ b/tests/unit/tests/unittests/projectstorage/qmldocumentparser-test.cpp @@ -3,6 +3,8 @@ #include "../utils/googletest.h" +#include + #include #include @@ -16,6 +18,7 @@ namespace Synchronization = Storage::Synchronization; using QmlDesigner::ModuleId; using QmlDesigner::SourceContextId; using QmlDesigner::SourceId; +using QmlDesigner::Storage::ModuleKind; MATCHER_P(HasPrototype, prototype, std::string(negation ? "isn't " : "is ") + PrintToString(prototype)) { @@ -143,7 +146,8 @@ class QmlDocumentParser : public ::testing::Test public: protected: Sqlite::Database database{":memory:", Sqlite::JournalMode::Memory}; - QmlDesigner::ProjectStorage storage{database, database.isInitialized()}; + ProjectStorageErrorNotifierMock errorNotifierMock; + QmlDesigner::ProjectStorage storage{database, errorNotifierMock, database.isInitialized()}; QmlDesigner::SourcePathCache sourcePathCache{ storage}; QmlDesigner::QmlDocumentParser parser{storage, sourcePathCache}; @@ -151,7 +155,7 @@ protected: SourceId qmlFileSourceId{sourcePathCache.sourceId("/path/to/qmlfile.qml")}; SourceContextId qmlFileSourceContextId{sourcePathCache.sourceContextId(qmlFileSourceId)}; Utils::PathString directoryPath{sourcePathCache.sourceContextPath(qmlFileSourceContextId)}; - ModuleId directoryModuleId{storage.moduleId(directoryPath)}; + ModuleId directoryModuleId{storage.moduleId(directoryPath, ModuleKind::PathLibrary)}; }; TEST_F(QmlDocumentParser, prototype) @@ -163,7 +167,7 @@ TEST_F(QmlDocumentParser, prototype) TEST_F(QmlDocumentParser, qualified_prototype) { - auto exampleModuleId = storage.moduleId("Example"); + auto exampleModuleId = storage.moduleId("Example", ModuleKind::QmlLibrary); QString text = R"(import Example 2.1 as Example Example.Item{})"; @@ -187,7 +191,7 @@ TEST_F(QmlDocumentParser, properties) TEST_F(QmlDocumentParser, qualified_properties) { - auto exampleModuleId = storage.moduleId("Example"); + auto exampleModuleId = storage.moduleId("Example", ModuleKind::QmlLibrary); auto type = parser.parse(R"(import Example 2.1 as Example Item{ property Example.Foo foo})", @@ -222,7 +226,7 @@ TEST_F(QmlDocumentParser, enumeration_in_properties) TEST_F(QmlDocumentParser, qualified_enumeration_in_properties) { - auto exampleModuleId = storage.moduleId("Example"); + auto exampleModuleId = storage.moduleId("Example", ModuleKind::QmlLibrary); auto type = parser.parse(R"(import Example 2.1 as Example Item{ property Example.Enumeration.Foo foo})", @@ -242,9 +246,9 @@ TEST_F(QmlDocumentParser, qualified_enumeration_in_properties) TEST_F(QmlDocumentParser, imports) { - ModuleId fooDirectoryModuleId = storage.moduleId("/path/foo"); - ModuleId qmlModuleId = storage.moduleId("QML"); - ModuleId qtQuickModuleId = storage.moduleId("QtQuick"); + ModuleId fooDirectoryModuleId = storage.moduleId("/path/foo", ModuleKind::PathLibrary); + ModuleId qmlModuleId = storage.moduleId("QML", ModuleKind::QmlLibrary); + ModuleId qtQuickModuleId = storage.moduleId("QtQuick", ModuleKind::QmlLibrary); auto type = parser.parse(R"(import QtQuick import "../foo" @@ -263,9 +267,9 @@ TEST_F(QmlDocumentParser, imports) TEST_F(QmlDocumentParser, imports_with_version) { - ModuleId fooDirectoryModuleId = storage.moduleId("/path/foo"); - ModuleId qmlModuleId = storage.moduleId("QML"); - ModuleId qtQuickModuleId = storage.moduleId("QtQuick"); + ModuleId fooDirectoryModuleId = storage.moduleId("/path/foo", ModuleKind::PathLibrary); + ModuleId qmlModuleId = storage.moduleId("QML", ModuleKind::QmlLibrary); + ModuleId qtQuickModuleId = storage.moduleId("QtQuick", ModuleKind::QmlLibrary); auto type = parser.parse(R"(import QtQuick 2.1 import "../foo" @@ -284,8 +288,8 @@ TEST_F(QmlDocumentParser, imports_with_version) TEST_F(QmlDocumentParser, imports_with_explict_directory) { - ModuleId qmlModuleId = storage.moduleId("QML"); - ModuleId qtQuickModuleId = storage.moduleId("QtQuick"); + ModuleId qmlModuleId = storage.moduleId("QML", ModuleKind::QmlLibrary); + ModuleId qtQuickModuleId = storage.moduleId("QtQuick", ModuleKind::QmlLibrary); auto type = parser.parse(R"(import QtQuick import "../to" @@ -358,10 +362,10 @@ TEST_F(QmlDocumentParser, enumeration) TEST_F(QmlDocumentParser, DISABLED_duplicate_imports_are_removed) { - ModuleId fooDirectoryModuleId = storage.moduleId("/path/foo"); - ModuleId qmlModuleId = storage.moduleId("QML"); - ModuleId qtQmlModuleId = storage.moduleId("QtQml"); - ModuleId qtQuickModuleId = storage.moduleId("QtQuick"); + ModuleId fooDirectoryModuleId = storage.moduleId("/path/foo", ModuleKind::PathLibrary); + ModuleId qmlModuleId = storage.moduleId("QML", ModuleKind::QmlLibrary); + ModuleId qtQmlModuleId = storage.moduleId("QtQml", ModuleKind::QmlLibrary); + ModuleId qtQuickModuleId = storage.moduleId("QtQuick", ModuleKind::QmlLibrary); auto type = parser.parse(R"(import QtQuick import "../foo" @@ -497,7 +501,7 @@ TEST_F(QmlDocumentParser, alias_on_list_property) TEST_F(QmlDocumentParser, qualified_list_property) { - auto exampleModuleId = storage.moduleId("Example"); + auto exampleModuleId = storage.moduleId("Example", ModuleKind::QmlLibrary); auto type = parser.parse(R"(import Example 2.1 as Example Item{ property list foos diff --git a/tests/unit/tests/unittests/projectstorage/qmltypesparser-test.cpp b/tests/unit/tests/unittests/projectstorage/qmltypesparser-test.cpp index 64f3631a68f..e75f7bf3a90 100644 --- a/tests/unit/tests/unittests/projectstorage/qmltypesparser-test.cpp +++ b/tests/unit/tests/unittests/projectstorage/qmltypesparser-test.cpp @@ -3,6 +3,8 @@ #include "../utils/googletest.h" +#include + #include #include @@ -17,6 +19,7 @@ namespace Synchronization = QmlDesigner::Storage::Synchronization; using QmlDesigner::ModuleId; using QmlDesigner::SourceContextId; using QmlDesigner::SourceId; +using QmlDesigner::Storage::ModuleKind; MATCHER_P3(IsImport, moduleId, @@ -168,20 +171,20 @@ class QmlTypesParser : public ::testing::Test public: protected: Sqlite::Database database{":memory:", Sqlite::JournalMode::Memory}; - QmlDesigner::ProjectStorage storage{database, database.isInitialized()}; + ProjectStorageErrorNotifierMock errorNotifierMock; + QmlDesigner::ProjectStorage storage{database, errorNotifierMock, database.isInitialized()}; QmlDesigner::SourcePathCache sourcePathCache{ storage}; QmlDesigner::QmlTypesParser parser{storage}; Storage::Imports imports; Synchronization::Types types; SourceId qmltypesFileSourceId{sourcePathCache.sourceId("path/to/types.qmltypes")}; - ModuleId qtQmlNativeModuleId = storage.moduleId("QtQml-cppnative"); - Synchronization::ProjectData projectData{qmltypesFileSourceId, + ModuleId qtQmlNativeModuleId = storage.moduleId("QtQml", ModuleKind::CppLibrary); + Synchronization::DirectoryInfo directoryInfo{qmltypesFileSourceId, qmltypesFileSourceId, qtQmlNativeModuleId, Synchronization::FileType::QmlTypes}; SourceContextId qmltypesFileSourceContextId{sourcePathCache.sourceContextId(qmltypesFileSourceId)}; - ModuleId directoryModuleId{storage.moduleId("path/to/")}; }; TEST_F(QmlTypesParser, imports) @@ -191,22 +194,22 @@ TEST_F(QmlTypesParser, imports) dependencies: ["QtQuick 2.15", "QtQuick.Window 2.1", "QtFoo 6"]})"}; - parser.parse(source, imports, types, projectData); + parser.parse(source, imports, types, directoryInfo); ASSERT_THAT(imports, - UnorderedElementsAre(IsImport(storage.moduleId("QML-cppnative"), + UnorderedElementsAre(IsImport(storage.moduleId("QML", ModuleKind::CppLibrary), QmlDesigner::Storage::Version{}, qmltypesFileSourceId), - IsImport(storage.moduleId("QtQml-cppnative"), + IsImport(storage.moduleId("QtQml", ModuleKind::CppLibrary), QmlDesigner::Storage::Version{}, qmltypesFileSourceId), - IsImport(storage.moduleId("QtQuick-cppnative"), + IsImport(storage.moduleId("QtQuick", ModuleKind::CppLibrary), QmlDesigner::Storage::Version{}, qmltypesFileSourceId), - IsImport(storage.moduleId("QtQuick.Window-cppnative"), + IsImport(storage.moduleId("QtQuick.Window", ModuleKind::CppLibrary), QmlDesigner::Storage::Version{}, qmltypesFileSourceId), - IsImport(storage.moduleId("QtFoo-cppnative"), + IsImport(storage.moduleId("QtFoo", ModuleKind::CppLibrary), QmlDesigner::Storage::Version{}, qmltypesFileSourceId))); } @@ -218,7 +221,7 @@ TEST_F(QmlTypesParser, types) Component { name: "QObject"} Component { name: "QQmlComponent"}})"}; - parser.parse(source, imports, types, projectData); + parser.parse(source, imports, types, directoryInfo); ASSERT_THAT(types, UnorderedElementsAre(IsType("QObject", @@ -241,7 +244,7 @@ TEST_F(QmlTypesParser, prototype) Component { name: "QQmlComponent" prototype: "QObject"}})"}; - parser.parse(source, imports, types, projectData); + parser.parse(source, imports, types, directoryInfo); ASSERT_THAT(types, UnorderedElementsAre(IsType("QObject", @@ -264,7 +267,7 @@ TEST_F(QmlTypesParser, extension) Component { name: "QQmlComponent" extension: "QObject"}})"}; - parser.parse(source, imports, types, projectData); + parser.parse(source, imports, types, directoryInfo); ASSERT_THAT(types, UnorderedElementsAre(IsType("QObject", @@ -286,10 +289,10 @@ TEST_F(QmlTypesParser, exported_types) Component { name: "QObject" exports: ["QML/QtObject 1.0", "QtQml/QtObject 2.1"] }})"}; - ModuleId qmlModuleId = storage.moduleId("QML"); - ModuleId qtQmlModuleId = storage.moduleId("QtQml"); + ModuleId qmlModuleId = storage.moduleId("QML", ModuleKind::QmlLibrary); + ModuleId qtQmlModuleId = storage.moduleId("QtQml", ModuleKind::QmlLibrary); - parser.parse(source, imports, types, projectData); + parser.parse(source, imports, types, directoryInfo); ASSERT_THAT( types, @@ -312,7 +315,7 @@ TEST_F(QmlTypesParser, properties) Property { name: "targets"; type: "QQuickItem"; isList: true; isReadonly: true; isPointer: true } }})"}; - parser.parse(source, imports, types, projectData); + parser.parse(source, imports, types, directoryInfo); ASSERT_THAT(types, ElementsAre(Field( @@ -346,7 +349,7 @@ TEST_F(QmlTypesParser, properties_with_qualified_types) Property { name: "values2"; type: "Qt::Vector" } }})"}; - parser.parse(source, imports, types, projectData); + parser.parse(source, imports, types, directoryInfo); ASSERT_THAT(types, Contains( @@ -372,7 +375,7 @@ TEST_F(QmlTypesParser, properties_without_type) Property { name: "target"; type: "QObject"; isPointer: true } }})"}; - parser.parse(source, imports, types, projectData); + parser.parse(source, imports, types, directoryInfo); ASSERT_THAT(types, ElementsAre( @@ -405,7 +408,7 @@ TEST_F(QmlTypesParser, functions) } }})"}; - parser.parse(source, imports, types, projectData); + parser.parse(source, imports, types, directoryInfo); ASSERT_THAT(types, ElementsAre(Field( @@ -436,7 +439,7 @@ TEST_F(QmlTypesParser, skip_java_script_functions) } }})"}; - parser.parse(source, imports, types, projectData); + parser.parse(source, imports, types, directoryInfo); ASSERT_THAT(types, ElementsAre(Field(&Synchronization::Type::functionDeclarations, IsEmpty()))); } @@ -456,7 +459,7 @@ TEST_F(QmlTypesParser, functions_with_qualified_types) } }})"}; - parser.parse(source, imports, types, projectData); + parser.parse(source, imports, types, directoryInfo); ASSERT_THAT(types, Contains( @@ -491,7 +494,7 @@ TEST_F(QmlTypesParser, signals) } }})"}; - parser.parse(source, imports, types, projectData); + parser.parse(source, imports, types, directoryInfo); ASSERT_THAT(types, ElementsAre(Field(&Synchronization::Type::signalDeclarations, @@ -524,7 +527,7 @@ TEST_F(QmlTypesParser, signals_with_qualified_types) } }})"}; - parser.parse(source, imports, types, projectData); + parser.parse(source, imports, types, directoryInfo); ASSERT_THAT(types, Contains( @@ -557,7 +560,7 @@ TEST_F(QmlTypesParser, enumerations) } }})"}; - parser.parse(source, imports, types, projectData); + parser.parse(source, imports, types, directoryInfo); ASSERT_THAT(types, Contains(Field( @@ -596,7 +599,7 @@ TEST_F(QmlTypesParser, enumeration_is_exported_as_type) exports: ["QML/QtObject 1.0", "QtQml/QtObject 2.1"] }})"}; - parser.parse(source, imports, types, projectData); + parser.parse(source, imports, types, directoryInfo); QmlDesigner::Storage::TypeTraits traits{QmlDesigner::Storage::TypeTraitsKind::Value}; traits.isEnum = true; @@ -642,7 +645,7 @@ TEST_F(QmlTypesParser, enumeration_is_exported_as_type_with_alias) exports: ["QML/QtObject 1.0", "QtQml/QtObject 2.1"] }})"}; - parser.parse(source, imports, types, projectData); + parser.parse(source, imports, types, directoryInfo); QmlDesigner::Storage::TypeTraits traits{QmlDesigner::Storage::TypeTraitsKind::Value}; traits.isEnum = true; @@ -690,7 +693,7 @@ TEST_F(QmlTypesParser, enumeration_is_exported_as_type_with_alias_too) exports: ["QML/QtObject 1.0", "QtQml/QtObject 2.1"] }})"}; - parser.parse(source, imports, types, projectData); + parser.parse(source, imports, types, directoryInfo); QmlDesigner::Storage::TypeTraits traits{QmlDesigner::Storage::TypeTraitsKind::Value}; traits.isEnum = true; @@ -728,7 +731,7 @@ TEST_F(QmlTypesParser, enumeration_is_referenced_by_qualified_name) } }})"}; - parser.parse(source, imports, types, projectData); + parser.parse(source, imports, types, directoryInfo); ASSERT_THAT(types, Contains(Field(&Synchronization::Type::propertyDeclarations, @@ -756,7 +759,7 @@ TEST_F(QmlTypesParser, alias_enumeration_is_referenced_by_qualified_name) } }})"}; - parser.parse(source, imports, types, projectData); + parser.parse(source, imports, types, directoryInfo); ASSERT_THAT(types, Contains(Field(&Synchronization::Type::propertyDeclarations, @@ -773,7 +776,7 @@ TEST_F(QmlTypesParser, access_type_is_reference) Component { name: "QObject" accessSemantics: "reference"}})"}; - parser.parse(source, imports, types, projectData); + parser.parse(source, imports, types, directoryInfo); ASSERT_THAT(types, ElementsAre(IsTypeTrait(Storage::TypeTraitsKind::Reference))); } @@ -785,7 +788,7 @@ TEST_F(QmlTypesParser, access_type_is_value) Component { name: "QObject" accessSemantics: "value"}})"}; - parser.parse(source, imports, types, projectData); + parser.parse(source, imports, types, directoryInfo); ASSERT_THAT(types, ElementsAre(IsTypeTrait(Storage::TypeTraitsKind::Value))); } @@ -797,7 +800,7 @@ TEST_F(QmlTypesParser, access_type_is_sequence) Component { name: "QObject" accessSemantics: "sequence"}})"}; - parser.parse(source, imports, types, projectData); + parser.parse(source, imports, types, directoryInfo); ASSERT_THAT(types, ElementsAre(IsTypeTrait(Storage::TypeTraitsKind::Sequence))); } @@ -809,7 +812,7 @@ TEST_F(QmlTypesParser, access_type_is_none) Component { name: "QObject" accessSemantics: "none"}})"}; - parser.parse(source, imports, types, projectData); + parser.parse(source, imports, types, directoryInfo); ASSERT_THAT(types, ElementsAre(IsTypeTrait(Storage::TypeTraitsKind::None))); } @@ -821,7 +824,7 @@ TEST_F(QmlTypesParser, uses_custom_parser) Component { name: "QObject" hasCustomParser: true }})"}; - parser.parse(source, imports, types, projectData); + parser.parse(source, imports, types, directoryInfo); ASSERT_THAT(types, ElementsAre(IsTypeTrait(UsesCustomParser(true)))); } @@ -833,7 +836,7 @@ TEST_F(QmlTypesParser, uses_no_custom_parser) Component { name: "QObject" hasCustomParser: false }})"}; - parser.parse(source, imports, types, projectData); + parser.parse(source, imports, types, directoryInfo); ASSERT_THAT(types, ElementsAre(IsTypeTrait(UsesCustomParser(false)))); } @@ -845,7 +848,7 @@ TEST_F(QmlTypesParser, default_property) Component { name: "QObject" defaultProperty: "children" }})"}; - parser.parse(source, imports, types, projectData); + parser.parse(source, imports, types, directoryInfo); ASSERT_THAT(types, ElementsAre(Field(&Synchronization::Type::defaultPropertyName, Eq("children")))); @@ -853,8 +856,8 @@ TEST_F(QmlTypesParser, default_property) TEST_F(QmlTypesParser, skip_template_item) { - ModuleId moduleId = storage.moduleId("QtQuick.Templates-cppnative"); - Synchronization::ProjectData projectData{qmltypesFileSourceId, + ModuleId moduleId = storage.moduleId("QtQuick.Templates", ModuleKind::CppLibrary); + Synchronization::DirectoryInfo directoryInfo{qmltypesFileSourceId, qmltypesFileSourceId, moduleId, Synchronization::FileType::QmlTypes}; @@ -863,7 +866,7 @@ TEST_F(QmlTypesParser, skip_template_item) Component { name: "QQuickItem"} Component { name: "QQmlComponent"}})"}; - parser.parse(source, imports, types, projectData); + parser.parse(source, imports, types, directoryInfo); ASSERT_THAT(types, UnorderedElementsAre(IsType("QQmlComponent", diff --git a/tests/unit/tests/unittests/projectstorage/typeannotationreader-test.cpp b/tests/unit/tests/unittests/projectstorage/typeannotationreader-test.cpp index a614a5c7cf2..d55c368f6ee 100644 --- a/tests/unit/tests/unittests/projectstorage/typeannotationreader-test.cpp +++ b/tests/unit/tests/unittests/projectstorage/typeannotationreader-test.cpp @@ -4,6 +4,7 @@ #include "../utils/googletest.h" #include +#include #include #include @@ -12,34 +13,47 @@ namespace { +using QmlDesigner::FlagIs; class TypeAnnotationReader : public testing::Test { protected: - static void SetUpTestSuite() + TypeAnnotationReader() { - static_database = std::make_unique(":memory:", Sqlite::JournalMode::Memory); - - static_projectStorage = std::make_unique( - *static_database, static_database->isInitialized()); + traits.canBeDroppedInFormEditor = FlagIs::True; + traits.canBeDroppedInNavigator = FlagIs::True; + traits.isMovable = FlagIs::True; + traits.isResizable = FlagIs::True; + traits.hasFormEditorItem = FlagIs::True; + traits.visibleInLibrary = FlagIs::True; } - static void TearDownTestSuite() - { - static_projectStorage.reset(); - static_database.reset(); - } + ~TypeAnnotationReader() { storage.resetForTestsOnly(); } - auto moduleId(Utils::SmallStringView name) const { return storage.moduleId(name); } + struct StaticData + { + Sqlite::Database database{":memory:", Sqlite::JournalMode::Memory}; + ProjectStorageErrorNotifierMock errorNotifierMock; + QmlDesigner::ProjectStorage storage{database, errorNotifierMock, database.isInitialized()}; + }; + + static void SetUpTestSuite() { staticData = std::make_unique(); } + + static void TearDownTestSuite() { staticData.reset(); } + + auto moduleId(Utils::SmallStringView name) const + { + return storage.moduleId(name, QmlDesigner::Storage::ModuleKind::QmlLibrary); + } protected: - inline static std::unique_ptr static_database; - Sqlite::Database &database = *static_database; - inline static std::unique_ptr static_projectStorage; - QmlDesigner::ProjectStorage &storage = *static_projectStorage; + inline static std::unique_ptr staticData; + Sqlite::Database &database = staticData->database; + QmlDesigner::ProjectStorage &storage = staticData->storage; QmlDesigner::Storage::TypeAnnotationReader reader{storage}; QmlDesigner::SourceId sourceId = QmlDesigner::SourceId::create(33); QmlDesigner::SourceId directorySourceId = QmlDesigner::SourceId::create(77); + QmlDesigner::Storage::TypeTraits traits; }; TEST_F(TypeAnnotationReader, parse_type) @@ -55,7 +69,6 @@ TEST_F(TypeAnnotationReader, parse_type) icon: "images/item-icon16.png" } })xy"}; - QmlDesigner::Storage::TypeTraits traits; auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId); @@ -92,7 +105,6 @@ TEST_F(TypeAnnotationReader, parse_true_canBeContainer) } } })xy"}; - QmlDesigner::Storage::TypeTraits traits; traits.canBeContainer = FlagIs::True; auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId); @@ -122,7 +134,6 @@ TEST_F(TypeAnnotationReader, parse_true_forceClip) } } })xy"}; - QmlDesigner::Storage::TypeTraits traits; traits.forceClip = FlagIs::True; auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId); @@ -152,7 +163,6 @@ TEST_F(TypeAnnotationReader, parse_true_doesLayoutChildren) } } })xy"}; - QmlDesigner::Storage::TypeTraits traits; traits.doesLayoutChildren = FlagIs::True; auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId); @@ -168,7 +178,7 @@ TEST_F(TypeAnnotationReader, parse_true_doesLayoutChildren) IsEmpty()))); } -TEST_F(TypeAnnotationReader, parse_true_canBeDroppedInFormEditor) +TEST_F(TypeAnnotationReader, parse_false_canBeDroppedInFormEditor) { using QmlDesigner::FlagIs; auto content = QString{R"xy( @@ -178,12 +188,11 @@ TEST_F(TypeAnnotationReader, parse_true_canBeDroppedInFormEditor) icon: "images/frame-icon16.png" Hints { - canBeDroppedInFormEditor: true + canBeDroppedInFormEditor: false } } })xy"}; - QmlDesigner::Storage::TypeTraits traits; - traits.canBeDroppedInFormEditor = FlagIs::True; + traits.canBeDroppedInFormEditor = FlagIs::False; auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId); @@ -198,7 +207,7 @@ TEST_F(TypeAnnotationReader, parse_true_canBeDroppedInFormEditor) IsEmpty()))); } -TEST_F(TypeAnnotationReader, parse_true_canBeDroppedInNavigator) +TEST_F(TypeAnnotationReader, parse_false_canBeDroppedInNavigator) { using QmlDesigner::FlagIs; auto content = QString{R"xy( @@ -208,12 +217,11 @@ TEST_F(TypeAnnotationReader, parse_true_canBeDroppedInNavigator) icon: "images/frame-icon16.png" Hints { - canBeDroppedInNavigator: true + canBeDroppedInNavigator: false } } })xy"}; - QmlDesigner::Storage::TypeTraits traits; - traits.canBeDroppedInNavigator = FlagIs::True; + traits.canBeDroppedInNavigator = FlagIs::False; auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId); @@ -242,7 +250,6 @@ TEST_F(TypeAnnotationReader, parse_true_canBeDroppedInView3D) } } })xy"}; - QmlDesigner::Storage::TypeTraits traits; traits.canBeDroppedInView3D = FlagIs::True; auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId); @@ -258,7 +265,7 @@ TEST_F(TypeAnnotationReader, parse_true_canBeDroppedInView3D) IsEmpty()))); } -TEST_F(TypeAnnotationReader, parse_true_isMovable) +TEST_F(TypeAnnotationReader, parse_false_isMovable) { using QmlDesigner::FlagIs; auto content = QString{R"xy( @@ -268,12 +275,11 @@ TEST_F(TypeAnnotationReader, parse_true_isMovable) icon: "images/frame-icon16.png" Hints { - isMovable: true + isMovable: false } } })xy"}; - QmlDesigner::Storage::TypeTraits traits; - traits.isMovable = FlagIs::True; + traits.isMovable = FlagIs::False; auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId); @@ -288,7 +294,7 @@ TEST_F(TypeAnnotationReader, parse_true_isMovable) IsEmpty()))); } -TEST_F(TypeAnnotationReader, parse_true_isResizable) +TEST_F(TypeAnnotationReader, parse_false_isResizable) { using QmlDesigner::FlagIs; auto content = QString{R"xy( @@ -298,12 +304,11 @@ TEST_F(TypeAnnotationReader, parse_true_isResizable) icon: "images/frame-icon16.png" Hints { - isResizable: true + isResizable: false } } })xy"}; - QmlDesigner::Storage::TypeTraits traits; - traits.isResizable = FlagIs::True; + traits.isResizable = FlagIs::False; auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId); @@ -318,7 +323,7 @@ TEST_F(TypeAnnotationReader, parse_true_isResizable) IsEmpty()))); } -TEST_F(TypeAnnotationReader, parse_true_hasFormEditorItem) +TEST_F(TypeAnnotationReader, parse_false_hasFormEditorItem) { using QmlDesigner::FlagIs; auto content = QString{R"xy( @@ -328,12 +333,11 @@ TEST_F(TypeAnnotationReader, parse_true_hasFormEditorItem) icon: "images/frame-icon16.png" Hints { - hasFormEditorItem: true + hasFormEditorItem: false } } })xy"}; - QmlDesigner::Storage::TypeTraits traits; - traits.hasFormEditorItem = FlagIs::True; + traits.hasFormEditorItem = FlagIs::False; auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId); @@ -362,7 +366,6 @@ TEST_F(TypeAnnotationReader, parse_true_isStackedContainer) } } })xy"}; - QmlDesigner::Storage::TypeTraits traits; traits.isStackedContainer = FlagIs::True; auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId); @@ -392,7 +395,6 @@ TEST_F(TypeAnnotationReader, parse_true_takesOverRenderingOfChildren) } } })xy"}; - QmlDesigner::Storage::TypeTraits traits; traits.takesOverRenderingOfChildren = FlagIs::True; auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId); @@ -422,7 +424,6 @@ TEST_F(TypeAnnotationReader, parse_true_visibleInNavigator) } } })xy"}; - QmlDesigner::Storage::TypeTraits traits; traits.visibleInNavigator = FlagIs::True; auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId); @@ -438,7 +439,7 @@ TEST_F(TypeAnnotationReader, parse_true_visibleInNavigator) IsEmpty()))); } -TEST_F(TypeAnnotationReader, parse_true_visibleInLibrary) +TEST_F(TypeAnnotationReader, parse_false_visibleInLibrary) { using QmlDesigner::FlagIs; auto content = QString{R"xy( @@ -448,12 +449,11 @@ TEST_F(TypeAnnotationReader, parse_true_visibleInLibrary) icon: "images/frame-icon16.png" Hints { - visibleInLibrary: true + visibleInLibrary: false } } })xy"}; - QmlDesigner::Storage::TypeTraits traits; - traits.visibleInLibrary = FlagIs::True; + traits.visibleInLibrary = FlagIs::False; auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId); @@ -478,11 +478,10 @@ TEST_F(TypeAnnotationReader, parse_false) icon: "images/frame-icon16.png" Hints { - isMovable: false + isMovable: true } } })xy"}; - QmlDesigner::Storage::TypeTraits traits; auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId); @@ -521,9 +520,9 @@ TEST_F(TypeAnnotationReader, parse_complex_expression) } } })xy"}; - QmlDesigner::Storage::TypeTraits frameTraits; + QmlDesigner::Storage::TypeTraits frameTraits = traits; frameTraits.isMovable = QmlDesigner::FlagIs::Set; - QmlDesigner::Storage::TypeTraits itemTraits; + QmlDesigner::Storage::TypeTraits itemTraits = traits; itemTraits.canBeContainer = QmlDesigner::FlagIs::True; auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId); @@ -573,7 +572,6 @@ TEST_F(TypeAnnotationReader, parse_item_library_entry) } } })xy"}; - QmlDesigner::Storage::TypeTraits traits; auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId); @@ -630,7 +628,6 @@ TEST_F(TypeAnnotationReader, parse_item_library_entry_with_properties) } } })xy"}; - QmlDesigner::Storage::TypeTraits traits; auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId); @@ -681,7 +678,6 @@ TEST_F(TypeAnnotationReader, parse_item_library_entry_template_path) } } })xy"}; - QmlDesigner::Storage::TypeTraits traits; auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId); @@ -734,7 +730,6 @@ TEST_F(TypeAnnotationReader, parse_item_library_entry_extra_file_paths) } } })xy"}; - QmlDesigner::Storage::TypeTraits traits; auto annotations = reader.parseTypeAnnotation(content, "/path", sourceId, directorySourceId); 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 815e9a0cc56..e362cdf8860 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 @@ -75,7 +75,12 @@ "*.png", "*.svg", "*.hdr", - "*.ktx" + "*.ktx", + "*.bmp", + "*.ttf", + "*.tiff", + "*.webp", + "*.gif" ], "mcuProperties": { }, @@ -93,7 +98,12 @@ "*.png", "*.svg", "*.hdr", - "*.ktx" + "*.ktx", + "*.bmp", + "*.ttf", + "*.tiff", + "*.webp", + "*.gif" ], "mcuProperties": { }, 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 2231f36ff21..80eaf6fefa3 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 @@ -45,7 +45,12 @@ "*.png", "*.svg", "*.hdr", - "*.ktx" + "*.ktx", + "*.bmp", + "*.ttf", + "*.tiff", + "*.webp", + "*.gif" ], "mcuProperties": { }, 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 815e9a0cc56..e362cdf8860 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 @@ -75,7 +75,12 @@ "*.png", "*.svg", "*.hdr", - "*.ktx" + "*.ktx", + "*.bmp", + "*.ttf", + "*.tiff", + "*.webp", + "*.gif" ], "mcuProperties": { }, @@ -93,7 +98,12 @@ "*.png", "*.svg", "*.hdr", - "*.ktx" + "*.ktx", + "*.bmp", + "*.ttf", + "*.tiff", + "*.webp", + "*.gif" ], "mcuProperties": { }, diff --git a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-mcu-1/testfile.qmltojson b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-mcu-1/testfile.qmltojson index 303bfc3899d..634623c9cf2 100644 --- a/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-mcu-1/testfile.qmltojson +++ b/tests/unit/tests/unittests/qmlprojectmanager/data/converter/test-set-mcu-1/testfile.qmltojson @@ -48,7 +48,12 @@ "*.png", "*.svg", "*.hdr", - "*.ktx" + "*.ktx", + "*.bmp", + "*.ttf", + "*.tiff", + "*.webp", + "*.gif" ], "mcuProperties": { }, diff --git a/tests/unit/tests/unittests/sqlite/sqlitestatement-test.cpp b/tests/unit/tests/unittests/sqlite/sqlitestatement-test.cpp index f533c651e96..24b20176eaa 100644 --- a/tests/unit/tests/unittests/sqlite/sqlitestatement-test.cpp +++ b/tests/unit/tests/unittests/sqlite/sqlitestatement-test.cpp @@ -269,6 +269,21 @@ TEST_F(SqliteStatement, bind_int_id) ASSERT_THAT(readStatement.fetchIntValue(0), 42); } +TEST_F(SqliteStatement, bind_special_state_id) +{ + enum class SpecialIdState { Unresolved = -1 }; + constexpr TestIntId unresolvedTypeId = TestIntId::createSpecialState(SpecialIdState::Unresolved); + SqliteTestStatement<0, 1> statement("INSERT INTO test VALUES ('id', 323, ?)", database); + + statement.bind(1, unresolvedTypeId); + statement.next(); + + SqliteTestStatement<1, 1> readStatement("SELECT value FROM test WHERE name='id'", database); + readStatement.next(); + ASSERT_THAT(readStatement.fetchType(0), Sqlite::Type::Integer); + ASSERT_THAT(readStatement.fetchIntValue(0), -1); +} + TEST_F(SqliteStatement, bind_invalid_long_long_id_to_null) { TestLongLongId id;